Copybara import of the project:

  - 6389c7c69453355ea6e3a998210e89361112d86f Initial commit by Eclipse Webmaster team <webmaster@eclipse.org>
  - b1e8ec8e2d4e657e9796cdd6b964359cb0d1cb8e Merge with old EclipseLink repository. History is preserv... by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 573e776b68cb7b52570b1314847efda1779823d3 README.md for GitHub by Dmitry Kornilov <maiden168@gmail.com>
  - d7ac18f33c71a9c236a176a3188c228957e83946 Main package renamed to org.eclipse.yasson by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 906d26722a85f4ce0e4b0611080b2affbf8a56a3 SPI service provider by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 98fb86162cf82afd25370e315bbd61ad6c978cb9 Sync from eclipselink/jsonb by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - d247e1cf9be519c9a05ddafe0b030c6b49f63f07 Uploading to Eclipse Nexus task added to the build script. by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - dc2d27a752d335de7ba04ef9381f7aa36978d981 Fix the error on serialization/deserialization of classse... by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 15a5d434c0923d6f5ac4e8edff25abfb0aded042 DatesTest fixes. by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - acda0ec7645c48bc579b2f187e2b866c8f0f8671 Fix issue #2 (#4) by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 98b6843d725b004042f6e88779e7e4b97e27cd8a Issue #3 - Fix a bug in deserialization of empty strings ... by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 8c67dec6e5bcd1e2c4b74c98c84837add34874af M1 release by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 6480a5834a7a2695b19230a081354eed63fda08f Version number restored by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - b1c75f280baae6fc0d981182d13107a60b5d38a4 Collection serialization optimizations by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 7067f86a503ac9696ea96d082a8b7918841eb037 Updated gradle wrapper, added opens for java.base/java.la... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 31da1073c2dca6b76aa6c52bd4a9c266b5a8da22 Added JDK9 module configuration. Project mavenized, build... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 49764aea60041ca146b69cd6263320f4c399b356 JsonbCreator fixed - JsonbProperty on parameters are now ... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - c57cae0a0226257587babbefbf346b54e6a87609 Add java.net public repository to access jsonb api artifa... by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - a2cec8fa7efc810b6b5a95599e555781ebc5aa4e JsonbCreator JsonbProperty is now not required by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - f9dcc80019743627b9ece2a741d148dbff21a21a Distribution management fixed in pom.xml. Snapshot versio... by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 6d68e91dacb8e8550c0c33206c3e0bc9822fe946 Fixed typos in class/method names (#12) by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 988dde7dfc496b63efe83e576ed16fc827df8962 Javadoc fixes. by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - cc4152b8c168d9bc781f4cd400e773c7660efbae Javadoc fixes. by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - ea995cd3096f84df9cacda8b8b095de89ef91699 Merge pull request #13 from moghaddam/yasson-issue-11 by Roman Grigoriadi <bravehorsie@gmail.com>
  - 7605393ff4fa88d106d9d7b327a46a5970e1ce38 yasson-6 nulls should be deserialized as empty optionals ... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - f00cc59b62d50bba983248a412808c982b3975f5 yasson-10 Fix JsonbDateFormat on getters/setters (#15) by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - eed57470a8b2c8a16eda8208c07e6f04ac45303c yasson-9 Fix time zone issue on integration tests (#16) by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 34a7103c630bb2f0df48372bfbcec3c9656759f2 Fixed moving parser for array user deserializer by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 088829c6570a30e88201a7af7ff954f6134563df Fixed partial property overrides by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - bedd4149a8f81d06377f5d437c08c433061434ed fixed deserialization ordering by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 70b566953b4f936a32e697f7fc6c742dd06d9a91 fixed deserialization for specific collection interfaces. by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 6e94399fdd813f5613d7c7106e98b5649c959709 Fix for float serialization + single value without wrappe... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 5a9c294888846111716c22df9cde4582a38055b7 yasson 9 Fix JsonbTransient on getters and setters (#17) by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 5e23aaee4d7d6fa29882ca542445fec2a15413ac Fixed deserialization of Optional objects by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 595b089d7fd6324dda646c1343fc47dbb8ae78ad Fixed serialization of BigDecimal upcasted to Number with... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - d69c65a3e1c204cf42d8413853a46a2d5fd27f07 Fixed jsonbconfig Nillable by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - d3056c7e13e819e6f73cdc9e9fa084a8cfeec652 Support for new property - FAIL_ON_UNKNOWN_PROPERTIES in ... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - f74b354792fa92916c2e39fd27d7be1a562c789e JSON-B API dependency changed to version 1.0.0-SNAPSHOT by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - db06a6f10241554c91ba17fb96cae01e7d9f79d8 Static methods starting with get are no longer treated as... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - fcc9b1e27160555af7dc7da76059947c69bcbbfe Fixed synthetic field recognition by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 7695e40983b39b60937f5a57a4773c070423a25b Fixed deserialization JsonObject into JsonValue type by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 920c85a8062590502597ff91fcb88064b6f80e08 Config properties externalized to wrapper class by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 1c024add880636a147d303b27bf70fd74b8970a2 Adapted FAIL_ON_UNKNOWN_PROPERTIES removal from API by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - f7ce859dc05df962ea95458ccfcb216f32574f37 Refactoring on date formats resolution by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 7bf9fe4f41b23667a3e4fe5ce19a8946f7982f1d IJson fixes, Date, Calendar by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 88caf35781c46d3be6a6c5d0bd05af770ddf24cd Fixed timezone issue on unit test to make it run properly... by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 754c84b0954d1c101d3844676fa892abf6309251 Undo changes which meant to be a fix for the junit failur... by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - d351dcc8c04a68e2d0c92334e045c96fefd17602 Instant respects JsonbDateFormat and iJson by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 86d55909733011ed2d36e56492d8da3be5f0a4ac java.time.DateTimeFormat is used to process java.util.Dat... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 2cdf427914a44f0669b29d2ec62f5c88c686e212 Fixed java.time.LocalDate with IJson by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 24aae6f76f325c3cb1f71f4d83839dba0f864aae Fixed local date time by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 1183bba458206743c73c811e3326ed3e317fda90 IJson single value test fixed by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 1364294b7b9a6bbdd3e2d676617a8312e733acd0 Fixed calendar deserializer in case time information is m... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - e371f555f1af9528b6abed3bf74e2deb0f559998 Merge pull request #19 from Verdent/master by Roman Grigoriadi <bravehorsie@gmail.com>
  - 4eca15291a5a86857b029aa30445303cdd76efef BigDecimal in Number fix (#20) by David Král <Verdent@users.noreply.github.com>
  - ae9efc7587409a6a287c1144114fa24b074b5059 Added some tests from TCK, fixed resource for zone offset... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - de95ab03527c152d8ebf02164253f0352840f217 Added test for Raw non collection generic bound, updated ... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - a9e2c54ec39eea4090c78d95952be693d053a9dd Fixed date locale test to work both on JDK8 and 9 by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 8b99d85d1b72e7734e22ba33938ff99b4b9bf7d5 All parameters from @JsonbCreator are now required to be ... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 9603fe1460e3b0eaf1ba122bab81f5c6f626b990 New test for property ordering by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - b6cd2e49c6a3abcb71157177e2e971013f97ac7c Deserialization of timezone by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 9b300f3211f6c7a0c56d52cec08456ccb765b00b JsonbException on error parsing dates by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - b0dd424fe08cd534192802322141558bb91271a6 PropertyOrdering for serialization, in case of inheritanc... by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 81b93e6361b050a322a9d1f4b70d48278eaf3fb9 Fixed typo in exception name. by Roman Grigoriadi <roman.grigoriadi@oracle.com>
  - 367ad6ca2e7d41f5d2b4e2e1b1de62bbab6ea47b Fixed formatting in README.md by Dmitry Kornilov <maiden168@gmail.com>
  - 93fce46f4a8643749cb7a2c666815d9f11dbe7a9 OSGi headers added by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 0115fbcaace54ffff46e73db047d5c2cf63c0428 pom.xml cleanup by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - b2c3186f9b235d371853c5e678d6f4c167309eb9 Fix OSGi headers generation on JDK9 by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 44c0b74c4914b43228e64659f8d510a5800c9f53 exported packages for OSGi limited to JsonBindingProvider... by Roman Grigoriadi <bravehorsie@gmail.com>
  - 9a59cc4174a12022e148e54804a12a17fe97d0a3 maven-bundle-plugin version changed to 3.2.0 to fix build... by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 007d7392648752d863700bba72527bb5cc4c26f8 Merge pull request #21 from Verdent/master by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 21e8f4596f17f9e9f45aa1db45645990b33593b2 Fixed typos in code and JavaDocs by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 50a0b7e1003f5d7cf2947918fc32896e2ac373da jsonb API version bumped to M2 by Roman Grigoriadi <bravehorsie@gmail.com>
  - 5206b0682ae0fa9d4be4414369d67cd6ac36a7d2 NPE when reading JSON with null value of an unmapped field by Roman Grigoriadi <bravehorsie@gmail.com>
  - ad18385aca94932440876885fe2e5acba9f2c2d0 Removed unused imports and do some minor cleanups by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 88c22fb9b8db106bbb9b42362b684d003f54fc4e Use JUnit rules instead of java assert by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 0e3978653e5829bb5ce19605ded01c226a3a65f7 Convert Optional properties of JsonBuilder to normal prop... by Ehsan Zaery Moghaddam <zaerymoghaddam@gmail.com>
  - 2f7133e7e8495b3993ac4dae20241c85a9b4b3a2 Module name changed to org.eclipse.yasson by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - e3f408f2f48db0cf9c3e7ad6854f215772f3067b javadoc generation fixed by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 5b3e2bfd8df3ccfe18433560783c7dfe67470fd1 Version 1.0.0-M2 release by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - b79e300af406e12b1d282f11651cc09c5c848d7d Switched to maven-deploy-plugin version 2.8.2 by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 55a294bbbd17750fd9e5b844964a50092a39d2a6 Restoring snapshot version by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - f6f46c1481f261c96c8d8beff028afaecf539907 Adopted exception rising wiht @JsonbTransient annotations... by Roman Grigoriadi <bravehorsie@gmail.com>
  - a0ea27b1770d4f73d568af6d530991d184cecb7a Added doPrivileged blocks + Test with security manager. by Roman Grigoriadi <bravehorsie@gmail.com>
  - d3a3b007896314d5b542d636d87b47b85b79e0c4 Fixed CDI tests on JPMS by Roman Grigoriadi <bravehorsie@gmail.com>
  - 4b2e6d8c8b4f53d8144fdb188e72f67181d6d780 Package name refactoring by Roman Grigoriadi <bravehorsie@gmail.com>
  - c5f46e754eb3ad8d8bd58fa46488f6e28de4ed83 Fixed CDI lookup by Roman Grigoriadi <bravehorsie@gmail.com>
  - 256918a159f123b4071244acd9d18a22ebceb03e Merge branch 'TODO-removal' by Roman Grigoriadi <bravehorsie@gmail.com>
  - f76576ba0d8b79c8226d365676c3076843b315b2 Include properties not present in @JsonbPropertyOrder in ... by Roman Grigoriadi <bravehorsie@gmail.com>
  - 7a3a11bccb31d857b8a2da53b314e7b4e7a9f405 Merge remote-tracking branch 'origin/master' by Roman Grigoriadi <bravehorsie@gmail.com>
  - f0ab6918a0f30ba5ef9c69a4ad798c42e64dda96 Updated link to the API project in README.md by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - ca29ee614809fcec88f4b618f20515ef4a8de1e6 Maven central release profile added by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 417e3a8e9936250cb4e0ace1dfa6da4afdae1dcc pom.xml: mailing lists fixed, license section updated by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 1dd443751fc027d68bf717933c9e7ac85c5d7ca7 Upgrading to JSON-B API 1.0.0-RC2 and JSON-P 1.1 by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 431dd54deec9446a2683e6832d2396aa1cbbf0ba Fixed runtime type resolve from AdaptedObjectSerializer, ... by Roman Grigoriadi <bravehorsie@gmail.com>
  - f522aa7dae04c56fdacc6b82cfd1967920b31097 Restoring SNAPSHOT version by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 5e04a8d77cb18f7a8b59b263276b2023d245454f Fixes #27 by Roman Grigoriadi <bravehorsie@gmail.com>
  - 2f4de027798a35bac7a5802182b3e55c1bd78443 cdi-api dependency updated to 2.0 by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - a1ef30408fb53417fcf256f465e78330dd22f264 Version 1.0.0-RC2 by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  - 804a688345559cab4bb6fd2a0d6f873c5b2d50db Restoring snapshot version by Dmitry Kornilov <dmitry.kornilov@oracle.com>
  (And 252 more changes)

GitOrigin-RevId: ee982a8d151ee53d0bb6424d1bc73380f06a74f0
Change-Id: I90c9c7250b18fc6a589c1546f5bd29aa77711bd1
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..cce0fc2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,25 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is. If there is an Exception, please include the full stack trace.
+
+**To Reproduce**
+Steps to reproduce the bug
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**System information:**
+ - OS: [e.g. Linux, Windows, Mac]
+ - Java Version: [e.g. 8, 11]
+ - Yasson Version: [e.g. 1.0.5]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..11fc491
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: enhancement
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/other-issue.md b/.github/ISSUE_TEMPLATE/other-issue.md
new file mode 100644
index 0000000..4e6bf5d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/other-issue.md
@@ -0,0 +1,10 @@
+---
+name: Other issue
+about: Not a bug or a feature request
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
new file mode 100644
index 0000000..3b21094
--- /dev/null
+++ b/.github/workflows/maven.yml
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2021, 2024 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,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+name: Yasson
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    name: Test on JDK ${{ matrix.java_version }}
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        java_version: [ 11, 17, 21 ]
+
+    steps:
+      - name: Checkout for build
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: Set up compile JDK
+        uses: actions/setup-java@v4
+        with: #Compile java needs to be the highest to ensure proper compilation of the multi-release jar
+          distribution: 'temurin'
+          java-version: 17
+          cache: 'maven'
+      - name: Copyright
+        run: bash etc/copyright.sh
+      - name: Checkstyle
+        run: mvn -B checkstyle:checkstyle -Pstaging
+      - name: Yasson install
+        run: mvn -U -C clean install -Pstaging -DskipTests
+      - name: Yasson tests
+        run: mvn -U -B -C -Dmaven.javadoc.skip=true -Pstaging verify
+      - name: JSONB-API TCK
+        run: cd yasson-tck && mvn -U -B test -DargLine="-Djava.locale.providers=COMPAT" -Pstaging
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3160630
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+/target/
+/target-tck/
+.classpath
+.project
+.idea/
+.settings/
+/.DS_Store
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..d2537d8
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,46 @@
+# Contributing to Eclipse Yasson
+
+Thanks for your interest in this project.
+
+## Project description
+
+Eclipse Yasson is a Java framework which provides a standard binding layer
+between Java classes and JSON documents. This is similar to what JAXB is doing
+in the XML world. Yasson is an official reference implementation of JSON Binding
+(JSR-367).
+
+* https://projects.eclipse.org/projects/ee4j.yasson
+
+## Developer resources
+
+Information regarding source code management, builds, coding standards, and
+more.
+
+* https://projects.eclipse.org/projects/ee4j.yasson/developer
+
+The project maintains the following source code repositories
+
+* https://github.com/eclipse/yasson
+* https://github.com/eclipse-ee4j/yasson
+
+## 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/yasson-dev
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..4cc1dfc
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,100 @@
+# 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.
+
+
+# Eclipse Distribution License - v 1.0
+
+    Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
+
+    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.
\ No newline at end of file
diff --git a/NOTICE.md b/NOTICE.md
new file mode 100644
index 0000000..2f615e8
--- /dev/null
+++ b/NOTICE.md
@@ -0,0 +1,45 @@
+# Notices for Eclipse Yasson
+
+This content is produced and maintained by the Eclipse Yasson project.
+
+* Project home: https://projects.eclipse.org/projects/ee4j.yasson
+
+## Trademarks
+
+Eclipse Yasson 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-v20.html, or the Eclipse Distribution License
+v. 1.0 which is available at http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+## Source Code
+
+The project maintains the following source code repositories:
+
+* https://github.com/eclipse/yasson
+* https://github.com/eclipse-ee4j/yasson
+
+## Third-party Content
+
+cdi-api 2.0 (JSR 365: Contexts and Dependency Injection for Java (2.0)
+
+
+## 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..6da2b0a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,50 @@
+# Eclipse Yasson
+
+[![Maven Central](https://img.shields.io/maven-central/v/org.eclipse/yasson.svg?label=Maven%20Central)](https://mvnrepository.com/artifact/org.eclipse/yasson)
+[![Jakarta Staging (Snapshots)](https://img.shields.io/nexus/s/https/jakarta.oss.sonatype.org/org.eclipse/yasson.svg)](https://jakarta.oss.sonatype.org/content/repositories/staging/org/eclipse/yasson/)
+[![Gitter](https://badges.gitter.im/eclipse/yasson.svg)](https://gitter.im/eclipse/yasson)
+[![Javadocs](https://www.javadoc.io/badge/org.eclipse/yasson.svg)](https://www.javadoc.io/doc/org.eclipse/yasson)
+[![Build Status](https://github.com/eclipse-ee4j/yasson/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/eclipse-ee4j/yasson/actions/workflows/maven.yml?branch=master)
+[![License](https://img.shields.io/badge/License-EPL%202.0-green.svg)](https://opensource.org/licenses/EPL-2.0)
+
+Yasson is a Java framework which provides a standard binding layer between Java classes and JSON documents. This is similar to what JAXB is doing in the XML world. Yasson is an official reference implementation of JSON Binding ([JSR-367](https://jcp.org/en/jsr/detail?id=367)).
+
+It defines a **default mapping** algorithm for converting existing Java classes to JSON suitable for the most cases:
+
+```java
+Jsonb jsonb = JsonbBuilder.create();
+String result = jsonb.toJson(someObject);
+```
+
+For whom it's not enough it provides rich customization abilities through a set of **annotations** and rich **programmatic API**: 
+
+```java
+// Create custom configuration
+JsonbConfig config = new JsonbConfig()
+  .withNullValues(true)
+  .withFormatting(true);
+
+// Create Jsonb with custom configuration
+Jsonb jsonb = JsonbBuilder.create(config);
+
+// Use it!
+String result = jsonb.toJson(someObject);
+```
+
+## Questions?
+
+Something not working right? Have an idea for an enhancement? Get in touch with the Yasson community in the following ways:
+
+- [Gitter](https://gitter.im/eclipse/yasson): a free instant-messaging platform (similar to Slack) that anyone can join.
+- [Stackoverflow](https://stackoverflow.com/questions/tagged/yasson): As a question tagged `[jsonb-api]` and `[yasson]`
+- [Github Issues](https://github.com/eclipse-ee4j/yasson/issues/new): Open issues for enhancement ideas or bug reports
+
+## Licenses
+- [Eclipse Distribution License 1.0 (BSD)](https://projects.eclipse.org/content/eclipse-distribution-license-1.0-bsd)
+- [Eclipse Public License 2.0](https://projects.eclipse.org/content/eclipse-public-license-2.0)
+
+## Links
+- Yasson home page: https://projects.eclipse.org/projects/ee4j.yasson
+- JSON-B official web site: https://eclipse-ee4j.github.io/jsonb-api/
+- JSON-B API & spec project: https://github.com/eclipse-ee4j/jsonb-api
+- JSR-367 page on JCP site: https://jcp.org/en/jsr/detail?id=367
diff --git a/etc/checkstyle-suppressions.xml b/etc/checkstyle-suppressions.xml
new file mode 100644
index 0000000..75682ad
--- /dev/null
+++ b/etc/checkstyle-suppressions.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!--
+
+    Copyright (c) 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,
+    or the Eclipse Distribution License v. 1.0 which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+-->
+
+<!DOCTYPE suppressions PUBLIC
+        "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
+        "https://checkstyle.org/dtds/suppressions_1_2.dtd">
+
+<suppressions>
+    <!-- I was not able to properly set import ordering for static imports -->
+    <suppress checks="ImportOrder" message="Extra separation in import group before" />
+</suppressions>
+
diff --git a/etc/checkstyle.xml b/etc/checkstyle.xml
new file mode 100644
index 0000000..69e6f2f
--- /dev/null
+++ b/etc/checkstyle.xml
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2019, 2024 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,
+    or the Eclipse Distribution License v. 1.0 which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+-->
+
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+<!--
+    This file is based on maven-checkstyle-plugin config/sun_checks.xml file.
+    Checkstyle configuration that checks the coding conventions based on:
+      - the Java Language Specification at
+        http://java.sun.com/docs/books/jls/second_edition/html/index.html
+      - the Sun Code Conventions at http://java.sun.com/docs/codeconv/
+      - the Javadoc guidelines at
+        http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
+      - the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
+      - some custom relaxations of the rules above & best practices
+    Checkstyle is very configurable. Be sure to read the documentation at
+    http://checkstyle.sf.net (or in your downloaded distribution).
+    Most Checks are configurable, be sure to consult the documentation.
+    To completely disable a check, just comment it out or delete it from the file.
+    Finally, it is worth reading the documentation.
+-->
+
+<module name="Checker">
+
+    <!-- exclude module-info.java check - checkstyle can't process it -->
+    <module name="BeforeExecutionExclusionFileFilter">
+        <property name="fileNamePattern" value="module\-info\.java$"/>
+    </module>
+
+    <!--
+        If you set the basedir property below, then all reported file
+        names will be relative to the specified directory. See
+        http://checkstyle.sourceforge.net/5.x/config.html#Checker
+        <property name="basedir" value="${basedir}"/>
+    -->
+    <property name="charset" value="UTF-8"/>
+
+    <!-- Checks that each Java package has a Javadoc file used for commenting. -->
+    <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage       -->
+<!--    <module name="JavadocPackage" />-->
+
+    <!-- Checks to see if a file contains a tab character. -->
+    <module name="FileTabCharacter">
+        <property name="eachLine" value="true"/>
+    </module>
+
+    <!--
+     Making sure we do not have @author tags in javadocs
+     There is a list of developers in pom.xml that is sufficient for our needs.
+     See https://github.com/checkstyle/checkstyle/issues/5339
+    -->
+    <module name="RegexpSingleline">
+        <!-- The '.' in id is needed, otherwise ArrayIndexOutOfBounds happens -->
+        <property name="id" value="Javadoc.javadocNoAuthor"/>
+        <property name="format" value="^\s*\*\s*@author"/>
+        <property name="minimum" value="0"/>
+        <property name="maximum" value="0"/>
+        <property name="message" value="Javadoc has illegal ''author'' tag."/>
+        <property name="fileExtensions" value="java"/>
+    </module>
+
+    <!-- Checks that property files contain the same keys.         -->
+    <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
+    <module name="Translation"/>
+
+    <module name="FileLength"/>
+
+    <module name="SuppressWarningsFilter"/>
+    <module name="TreeWalker">
+        <module name="SuppressionCommentFilter">
+            <property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)"/>
+            <property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)"/>
+            <property name="checkFormat" value="$1"/>
+        </module>
+        <module name="SuppressWarningsHolder"/>
+
+        <!-- Checks for Javadoc comments.                     -->
+        <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+        <module name="JavadocMethod">
+            <property name="accessModifiers" value="protected"/>
+            <property name="allowMissingReturnTag" value="true"/>
+            <property name="allowMissingParamTags" value="true"/>
+        </module>
+        <module name="JavadocType">
+            <property name="scope" value="protected"/>
+        </module>
+        <module name="JavadocVariable">
+            <property name="scope" value="protected"/>
+        </module>
+        <module name="JavadocStyle"/>
+
+        <!-- Checks for Naming Conventions.                  -->
+        <!-- See http://checkstyle.sf.net/config_naming.html -->
+        <module name="ConstantName"/>
+        <module name="LocalFinalVariableName"/>
+        <module name="LocalVariableName"/>
+        <module name="MemberName"/>
+        <module name="MethodName"/>
+        <module name="PackageName"/>
+        <module name="ParameterName"/>
+        <module name="StaticVariableName"/>
+        <module name="TypeName"/>
+
+        <!-- Checks for imports                              -->
+        <!-- See http://checkstyle.sf.net/config_import.html -->
+        <module name="AvoidStarImport"/>
+        <module name="UnusedImports"/>
+        <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+        <module name="RedundantImport"/>
+        <module name="ImportOrder">
+            <property name="groups" value="java, javax, jakarta, org.eclipse.yasson"/>
+            <property name="ordered" value="true"/>
+            <property name="separated" value="true"/>
+            <property name="option" value="bottom"/>
+        </module>
+
+        <!-- Checks for blocks. You know, those {}'s         -->
+        <!-- See http://checkstyle.sf.net/config_blocks.html -->
+        <module name="AvoidNestedBlocks"/>
+        <module name="EmptyBlock">
+            <property name="option" value="TEXT"/>
+            <property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
+        </module>
+        <module name="NeedBraces">
+            <property name="allowSingleLineStatement" value="true"/>
+        </module>
+        <module name="LeftCurly"/>
+        <module name="RightCurly"/>
+        <module name="RightCurly">
+            <property name="option" value="alone"/>
+            <property name="tokens"
+                    value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
+        </module>
+
+        <!-- Indentation -->
+        <property name="tabWidth" value="4"/>
+
+        <!-- Wrapping Lines -->
+        <module name="NoLineWrap"/>
+        <module name="SeparatorWrap">
+            <property name="tokens" value="DOT"/>
+            <property name="option" value="nl"/>
+        </module>
+        <module name="SeparatorWrap">
+            <property name="tokens" value="COMMA"/>
+            <property name="option" value="EOL"/>
+        </module>
+
+        <!-- Several variable declaration on one line: int i, p; -->
+        <module name="MultipleVariableDeclarations"/>
+
+        <module name="OuterTypeFilename"/>
+        <module name="OneTopLevelClass"/>
+
+        <!-- Checks for whitespace                               -->
+        <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+        <module name="OperatorWrap"/>
+        <module name="WhitespaceAfter"/>
+        <module name="MethodParamPad"/>
+        <module name="NoWhitespaceAfter"/>
+        <module name="NoWhitespaceBefore"/>
+        <module name="ParenPad"/>
+        <module name="TypecastParenPad"/>
+        <module name="WhitespaceAfter"/>
+        <module name="WhitespaceAround">
+            <!-- Removed static initializer issues: RCURLY, SLIST -->
+            <property name="tokens"
+                    value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV,
+                            DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAND, LCURLY, LE, LITERAL_ASSERT, LITERAL_CATCH, LITERAL_DO,
+                            LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH,
+                            LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
+                            NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, SL, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN,
+                            TYPE_EXTENSION_AND"/>
+            <property name="allowEmptyConstructors" value="true"/>
+            <property name="allowEmptyMethods" value="true"/>
+            <property name="allowEmptyTypes" value="true"/>
+            <property name="allowEmptyLoops" value="true"/>
+            <message key="ws.notFollowed"
+                    value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
+            <message key="ws.notPreceded"
+                    value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
+        </module>
+
+        <module name="GenericWhitespace">
+            <message key="ws.followed"
+                    value="GenericWhitespace ''{0}'' is followed by whitespace."/>
+            <message key="ws.preceded"
+                    value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
+            <message key="ws.illegalFollow"
+                    value="GenericWhitespace ''{0}'' should followed by whitespace."/>
+            <message key="ws.notPreceded"
+                    value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
+        </module>
+
+        <!-- Modifier Checks                                    -->
+        <!-- See http://checkstyle.sf.net/config_modifiers.html -->
+        <module name="ModifierOrder"/>
+        <module name="RedundantModifier"/>
+
+        <!-- Checks for common coding problems               -->
+        <!-- See http://checkstyle.sf.net/config_coding.html -->
+        <module name="EmptyStatement"/>
+        <module name="EqualsHashCode"/>
+        <module name="IllegalInstantiation"/>
+        <module name="InnerAssignment"/>
+        <module name="MissingSwitchDefault"/>
+
+        <module name="SimplifyBooleanExpression"/>
+        <module name="SimplifyBooleanReturn"/>
+
+        <!-- Checks for class design                         -->
+        <!-- See http://checkstyle.sf.net/config_design.html -->
+        <module name="HideUtilityClassConstructor"/>
+        <module name="InterfaceIsType"/>
+        <module name="VisibilityModifier">
+            <property name="packageAllowed" value="true"/>
+        </module>
+        <module name="ThrowsCount">
+            <property name="max" value="3"/>
+        </module>
+
+        <!-- Miscellaneous other checks.                   -->
+        <!-- See http://checkstyle.sf.net/config_misc.html -->
+        <module name="ArrayTypeStyle"/>
+        <module name="TodoComment"/>
+        <module name="UpperEll"/>
+
+        <module name="OneStatementPerLine"/>
+<!--        <module name="FallThrough"/>-->
+
+        <module name="NoFinalizer"/>
+    </module>
+</module>
diff --git a/etc/copyright-exclude.txt b/etc/copyright-exclude.txt
new file mode 100644
index 0000000..ab54d67
--- /dev/null
+++ b/etc/copyright-exclude.txt
@@ -0,0 +1,32 @@
+.iml
+.apt
+.args
+.bundle
+.class
+.ddl
+.exe
+.gif
+.gitignore
+.ico
+.jar
+.jks
+.jpg
+.json
+.mm
+.ods
+.png
+.svg
+.war
+.zip
+.dat
+.md
+.p12
+.txt
+.mf
+.pem
+.p8
+.pkcs8.pem
+.p12
+.bin
+.vm
+.policy
diff --git a/etc/copyright.sh b/etc/copyright.sh
new file mode 100755
index 0000000..a7ded09
--- /dev/null
+++ b/etc/copyright.sh
@@ -0,0 +1,23 @@
+#!/bin/bash -x
+#
+# Copyright (c) 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,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+die(){ echo "${1}" ; exit 1 ;}
+
+readonly RESULT_FILE="target/copyright-check.txt"
+mkdir target
+
+mvn -q org.glassfish.copyright:glassfish-copyright-maven-plugin:copyright \
+        > ${RESULT_FILE} || (cat ${RESULT_FILE}; die "Error running the Maven command")
+
+grep -i "copyright" ${RESULT_FILE} \
+    && die "COPYRIGHT ERROR" || echo "COPYRIGHT OK"
diff --git a/etc/copyright.txt b/etc/copyright.txt
new file mode 100644
index 0000000..1f0ccf3
--- /dev/null
+++ b/etc/copyright.txt
@@ -0,0 +1,11 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
\ No newline at end of file
diff --git a/etc/delivery-checks.sh b/etc/delivery-checks.sh
new file mode 100755
index 0000000..55eab26
--- /dev/null
+++ b/etc/delivery-checks.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright (c) 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,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+mvn checkstyle:checkstyle && \
+etc/copyright.sh && \
+echo "Completed normally"
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..5e49092
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,634 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2016, 2024 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,
+    or the Eclipse Distribution License v. 1.0 which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: EPL-2.0 OR 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/xsd/maven-4.0.0.xsd">
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.9</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.eclipse</groupId>
+    <artifactId>yasson</artifactId>
+    <version>3.0.4</version>
+    <packaging>jar</packaging>
+    <name>Yasson</name>
+
+    <description>Eclipse Yasson. Reference implementation of JSR-367 (JSON-B).</description>
+    <url>https://projects.eclipse.org/projects/ee4j.yasson</url>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.release>11</maven.compiler.release>
+        <maven.compiler.testRelease>${maven.compiler.release}</maven.compiler.testRelease>
+
+        <!--Dependencies-->
+        <hamcrest.version>2.2</hamcrest.version>
+        <jakarta.annotation-api.version>3.0.0</jakarta.annotation-api.version>
+        <jakarta.el-api.version>6.0.0</jakarta.el-api.version>
+        <jakarta.enterprise.cdi-api.version>4.1.0</jakarta.enterprise.cdi-api.version>
+        <jakarta.interceptor-api.version>2.2.0</jakarta.interceptor-api.version>
+        <jakarta.json.bind.version>3.0.1</jakarta.json.bind.version>
+        <jakarta.json.version>2.1.3</jakarta.json.version>
+        <jakarta.parson.version>1.1.7</jakarta.parson.version>
+        <junit-jupiter.version>5.10.2</junit-jupiter.version>
+        <weld-se-core.version>6.0.0.Beta1</weld-se-core.version>
+
+        <!--Plugins-->
+        <build-helper-maven-plugin.version>3.6.0</build-helper-maven-plugin.version>
+        <buildnumber-maven-plugin.version>3.2.0</buildnumber-maven-plugin.version>
+        <spotbugs-maven-plugin.version>4.8.5.0</spotbugs-maven-plugin.version>
+        <glassfish-copyright-maven-plugin.version>2.4</glassfish-copyright-maven-plugin.version>
+        <maven-bundle-plugin.version>5.1.9</maven-bundle-plugin.version>
+        <maven-checkstyle-plugin.version>3.3.1</maven-checkstyle-plugin.version>
+        <maven-compiler-plugin.version>3.13.0</maven-compiler-plugin.version>
+        <maven-enforcer-plugin.version>3.4.1</maven-enforcer-plugin.version>
+        <maven-jar-plugin.version>3.4.1</maven-jar-plugin.version>
+        <maven-javadoc-plugin.version>3.6.3</maven-javadoc-plugin.version>
+        <maven-surefire-plugin.version>3.2.5</maven-surefire-plugin.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>jakarta.enterprise</groupId>
+                <artifactId>jakarta.enterprise.cdi-api</artifactId>
+                <version>${jakarta.enterprise.cdi-api.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.el</groupId>
+                <artifactId>jakarta.el-api</artifactId>
+                <version>${jakarta.el-api.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.interceptor</groupId>
+                <artifactId>jakarta.interceptor-api</artifactId>
+                <version>${jakarta.interceptor-api.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>jakarta.annotation</groupId>
+                <artifactId>jakarta.annotation-api</artifactId>
+                <version>${jakarta.annotation-api.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- Compile dependencies -->
+        <dependency>
+            <groupId>jakarta.json.bind</groupId>
+            <artifactId>jakarta.json.bind-api</artifactId>
+            <version>${jakarta.json.bind.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <version>${jakarta.json.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.parsson</groupId>
+            <artifactId>parsson</artifactId>
+            <version>${jakarta.parson.version}</version>
+        </dependency>
+        <!-- Test/Provided dependencies -->
+        <dependency>
+            <groupId>jakarta.enterprise</groupId>
+            <artifactId>jakarta.enterprise.cdi-api</artifactId>
+            <optional>true</optional>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.weld.se</groupId>
+            <artifactId>weld-se-core</artifactId>
+            <version>${weld-se-core.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit-jupiter.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit-jupiter.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <version>${hamcrest.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <organization>
+        <name>Oracle Corporation</name>
+        <url>http://www.oracle.com/</url>
+    </organization>
+
+    <issueManagement>
+        <system>github</system>
+        <url>https://github.com/eclipse-ee4j/yasson/issues</url>
+    </issueManagement>
+
+    <mailingLists>
+        <mailingList>
+            <name>Yasson mailing list</name>
+            <post>yasson-dev@eclipse.org</post>
+            <subscribe>https://dev.eclipse.org/mailman/listinfo/yasson-dev</subscribe>
+            <unsubscribe>https://dev.eclipse.org/mailman/listinfo/yasson-dev</unsubscribe>
+            <archive>https://dev.eclipse.org/mhonarc/lists/yasson-dev/</archive>
+        </mailingList>
+    </mailingLists>
+
+    <licenses>
+        <license>
+            <name>Eclipse Public License v. 2.0</name>
+            <url>http://www.eclipse.org/legal/epl-v20.html</url>
+            <distribution>repo</distribution>
+            <comments>Standard Eclipse License</comments>
+        </license>
+        <license>
+            <name>Eclipse Distribution License v. 1.0</name>
+            <url>http://www.eclipse.org/org/documents/edl-v10.php</url>
+            <distribution>repo</distribution>
+            <comments>Standard Eclipse Distribution License</comments>
+        </license>
+    </licenses>
+
+    <scm>
+        <connection>scm:git:ssh://git@github.com/eclipse-ee4j/yasson.git</connection>
+        <developerConnection>scm:git:ssh://git@github.com/eclipse-ee4j/yasson.git</developerConnection>
+        <url>https://github.com/eclipse-ee4j/yasson.git</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <developers>
+        <developer>
+            <email>dmitry.kornilov@oracle.com</email>
+            <id>maiden168</id>
+            <name>Dmitry Kornilov</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>JSON Binding 1.0 Spec Lead</role>
+            </roles>
+            <timezone>CET</timezone>
+        </developer>
+        <developer>
+            <email>roman.grigoriadi@oracle.com</email>
+            <id>roman.grigoriadi</id>
+            <name>Roman Grigoriadi</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>JSON Binding 1.0 Developer</role>
+            </roles>
+            <timezone>CET</timezone>
+        </developer>
+        <developer>
+            <email>david.k.kral@oracle.com</email>
+            <id>david.kral</id>
+            <name>David Kral</name>
+            <organization>Oracle</organization>
+            <roles>
+                <role>JSON Binding 1.0 Developer</role>
+            </roles>
+            <timezone>CET</timezone>
+        </developer>
+        <developer>
+            <email>andy.guibert@gmail.com</email>
+            <id>aguibert</id>
+            <name>Andy Guibert</name>
+            <organization>IBM</organization>
+            <roles>
+                <role>JSON Binding 1.0 Developer</role>
+            </roles>
+            <timezone>CST</timezone>
+        </developer>
+    </developers>
+
+    <profiles>
+        <profile>
+            <id>checkstyle</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-checkstyle-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>run-checkstyle</id>
+                                <goals>
+                                    <goal>check</goal>
+                                </goals>
+                                <phase>validate</phase>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>spotbugs</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>com.github.spotbugs</groupId>
+                        <artifactId>spotbugs-maven-plugin</artifactId>
+                        <version>${spotbugs-maven-plugin.version}</version>
+                        <configuration>
+                            <threshold>Low</threshold>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>analyze-compile</id>
+                                <phase>compile</phase>
+                                <goals>
+                                    <goal>check</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>copyright</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.glassfish.copyright</groupId>
+                        <artifactId>glassfish-copyright-maven-plugin</artifactId>
+                        <executions>
+                            <!--Copyright goal does not fail when copyright is not properly updated,
+                                but prints out which file has incorrect copyright.-->
+                            <execution>
+                                <id>print-copyright</id>
+                                <goals>
+                                    <goal>copyright</goal>
+                                </goals>
+                                <phase>validate</phase>
+                            </execution>
+                            <!--Check goal does not print out incorrect copyright files, but fails when there are errors.-->
+                            <execution>
+                                <id>check-copyright</id>
+                                <goals>
+                                    <goal>check</goal>
+                                </goals>
+                                <phase>validate</phase>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>jdk16</id>
+            <activation>
+                <jdk>[16,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>default-testCompile</id>
+                                <configuration>
+                                    <release>16</release>
+                                    <testRelease>16</testRelease>
+                                    <compileSourceRoots>
+                                        <compileSourceRoot>${project.basedir}/src/test/java</compileSourceRoot>
+                                        <compileSourceRoot>${project.basedir}/src/test/java16</compileSourceRoot>
+                                    </compileSourceRoots>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <includes>
+                                <include>**/RecordTest.java</include>
+                            </includes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <build>
+        <finalName>${project.artifactId}</finalName>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>${maven-compiler-plugin.version}</version>
+                    <!-- defaults for compile and testCompile -->
+                    <executions>
+                        <execution>
+                            <id>default-compile</id>
+                            <goals>
+                                <goal>compile</goal>
+                            </goals>
+                            <configuration>
+                                <release>11</release>
+                                <source>11</source>
+                                <target>11</target>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>default-testCompile</id>
+                            <configuration>
+                                <release>11</release>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>multi-release-compile-16</id>
+                            <goals>
+                                <goal>compile</goal>
+                            </goals>
+                            <configuration>
+                                <release>16</release>
+                                <compileSourceRoots>
+                                    <compileSourceRoot>${project.basedir}/src/main/java16</compileSourceRoot>
+                                </compileSourceRoots>
+                                <multiReleaseOutput>true</multiReleaseOutput>
+                            </configuration>
+                        </execution>
+                    </executions>
+                    <configuration>
+                        <compilerArgs>
+                            <arg>-Xlint:all</arg>
+                        </compilerArgs>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <version>${maven-jar-plugin.version}</version>
+                    <configuration>
+                        <archive>
+                            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                            <manifestEntries>
+                                <Multi-Release>true</Multi-Release>
+                            </manifestEntries>
+                        </archive>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <!-- This plugin generates the buildNumber property used in maven-bundle-plugin -->
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>buildnumber-maven-plugin</artifactId>
+                    <version>${buildnumber-maven-plugin.version}</version>
+                    <configuration>
+                        <format>{0,date,MM/dd/yyyy hh:mm aa}</format>
+                        <items>
+                            <item>timestamp</item>
+                        </items>
+                    </configuration>
+                    <executions>
+                        <execution>
+                            <phase>validate</phase>
+                            <goals>
+                                <goal>create</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <version>${maven-javadoc-plugin.version}</version>
+                    <configuration>
+                        <bottom><![CDATA[Copyright &#169; 2017, 2024 Oracle Corporation. All rights reserved.<br>]]></bottom>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>${maven-bundle-plugin.version}</version>
+                    <executions>
+                        <execution>
+                            <id>osgi-bundle</id>
+                            <phase>prepare-package</phase>
+                            <goals>
+                                <goal>manifest</goal>
+                            </goals>
+                            <configuration>
+                                <niceManifest>true</niceManifest>
+                                <instructions>
+                                    <Bundle-Name>${project.name}</Bundle-Name>
+                                    <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                                    <Bundle-Version>${project.version}</Bundle-Version>
+                                    <Export-Package>org.eclipse.yasson;version=${project.version}</Export-Package>
+                                    <Private-Package>org.eclipse.yasson.*;version=${project.version}</Private-Package>
+                                    <Multi-Release>true</Multi-Release>
+                                    <Import-Package>
+                                        jakarta.enterprise.context.spi;version=!;resolution:="optional",
+                                        jakarta.enterprise.inject.spi;version=!;resolution:="optional",
+                                        javax.naming;resolution:="optional",
+                                        java.beans;resolution:="optional",
+                                        *
+                                    </Import-Package>
+                                    <Require-Capability>osgi.ee;filter:="(&amp;(osgi.ee=JavaSE)(version=11))"</Require-Capability>
+                                </instructions>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${maven-surefire-plugin.version}</version>
+                    <executions>
+                        <execution>
+                            <id>default-test</id>
+                            <phase>test</phase>
+                            <goals>
+                                <goal>test</goal>
+                            </goals>
+                            <configuration>
+<!--                                <testFailureIgnore>true</testFailureIgnore>-->
+                                <trimStackTrace>false</trimStackTrace>
+                                <excludes>
+                                    <exclude>**/JavaxNamingExcludedTest.java</exclude>
+                                    <exclude>**/AnnotationIntrospectorWithoutOptionalModulesTest.java</exclude>
+                                    <exclude>**/*Record*</exclude>
+                                </excludes>
+                                <argLine>
+                                    <!--Remove when CDI is updated to support modules
+                                    (Yasson does read CDI which is on CP, CDI access yasson's classes with reflection)
+                                    -->
+                                    --add-reads org.eclipse.yasson=ALL-UNNAMED --add-opens org.eclipse.yasson/org.eclipse.yasson.internal.cdi=ALL-UNNAMED
+
+                                    --add-exports org.eclipse.yasson/org.eclipse.yasson.internal.cdi=java.naming
+                                </argLine>
+                            </configuration>
+                        </execution>
+                        <execution>
+                            <id>optional-modules-excluded</id>
+                            <phase>test</phase>
+                            <goals>
+                                <goal>test</goal>
+                            </goals>
+                            <configuration>
+                                <argLine>
+                                    <!--java.management is required by Maven's ForkedBooter-->
+                                    --limit-modules java.base,java.logging,java.sql,jakarta.json.bind,jakarta.json,java.management,jdk.localedata,org.eclipse.parsson,org.eclipse.yasson
+                                </argLine>
+                                <includes>
+                                    <include>**/JavaxNamingExcludedTest.class</include>
+                                    <include>**/AnnotationIntrospectorWithoutOptionalModulesTest.class</include>
+                                </includes>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-enforcer-plugin</artifactId>
+                    <version>${maven-enforcer-plugin.version}</version>
+                    <executions>
+                        <execution>
+                            <id>enforce-versions</id>
+                            <goals>
+                                <goal>enforce</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                    <configuration>
+                        <rules>
+                            <requireJavaVersion>
+                                <version>[11,)</version>
+                            </requireJavaVersion>
+                            <requireMavenVersion>
+                                <version>[3.6.0,)</version>
+                            </requireMavenVersion>
+                            <DependencyConvergence/>
+                        </rules>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>build-helper-maven-plugin</artifactId>
+                    <version>${build-helper-maven-plugin.version}</version>
+                    <executions>
+                        <execution>
+                            <id>add-resource</id>
+                            <phase>generate-resources</phase>
+                            <goals>
+                                <goal>add-resource</goal>
+                            </goals>
+                            <configuration>
+                                <resources>
+                                    <resource>
+                                        <directory>${basedir}</directory>
+                                        <targetPath>META-INF</targetPath>
+                                        <includes>
+                                            <include>LICENSE.md</include>
+                                            <include>NOTICE.md</include>
+                                        </includes>
+                                    </resource>
+                                </resources>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-checkstyle-plugin</artifactId>
+                    <version>${maven-checkstyle-plugin.version}</version>
+                    <configuration>
+                        <configLocation>etc/checkstyle.xml</configLocation>
+                        <suppressionsLocation>etc/checkstyle-suppressions.xml</suppressionsLocation>
+                        <consoleOutput>true</consoleOutput>
+                        <failsOnError>true</failsOnError>
+                        <linkXRef>false</linkXRef>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.glassfish.copyright</groupId>
+                    <artifactId>glassfish-copyright-maven-plugin</artifactId>
+                    <version>${glassfish-copyright-maven-plugin.version}</version>
+                    <configuration>
+                        <templateFile>etc/copyright.txt</templateFile>
+                        <excludeFile>etc/copyright-exclude.txt</excludeFile>
+                        <scm>git</scm>
+                        <debug>false</debug>
+                        <scmOnly>true</scmOnly>
+                        <warn>true</warn>
+                        <ignoreYear>false</ignoreYear>
+                        <preserveCopyrights>true</preserveCopyrights>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>buildnumber-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..12366c5
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/**
+ * Yasson, the implementation of the Jakarta JSON Binding.
+ */
+module org.eclipse.yasson {
+    requires jakarta.json;
+    requires jakarta.json.bind;
+    requires java.logging;
+    requires static java.xml;
+    requires static java.naming;
+    requires static java.sql;
+    requires static java.desktop;
+    requires static jakarta.cdi;
+
+    exports org.eclipse.yasson;
+    exports org.eclipse.yasson.spi;
+    provides jakarta.json.bind.spi.JsonbProvider with org.eclipse.yasson.JsonBindingProvider;
+    uses org.eclipse.yasson.spi.JsonbComponentInstanceCreator;
+}
diff --git a/src/main/java/org/eclipse/yasson/FieldAccessStrategy.java b/src/main/java/org/eclipse/yasson/FieldAccessStrategy.java
new file mode 100644
index 0000000..1ce711f
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/FieldAccessStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+
+/**
+ * <p>Strategy that can be used to force always using fields instead of getters setters for getting / setting value.</p>
+ * <p>Suggested approach is to use default visibility strategy, which will use public getters / setters, or field
+ * if it is public.</p>
+ *
+ * <p>Please consider, that forcing accessing fields will in most cases (when field is not public)
+ * result in calling {@link Field#setAccessible(boolean)} to break into clients code.
+ * This may cause problems if client code is loaded as JPMS (Java Platform Module System) module, as OSGi module or
+ * when SecurityManager is turned on.</p>
+ */
+public class FieldAccessStrategy implements PropertyVisibilityStrategy {
+    @Override
+    public boolean isVisible(Field field) {
+        return true;
+    }
+
+    @Override
+    public boolean isVisible(Method method) {
+        return false;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/ImplementationClass.java b/src/main/java/org/eclipse/yasson/ImplementationClass.java
new file mode 100644
index 0000000..bb36c3c
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/ImplementationClass.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks an interface with implementation class, which should be used for deserialiation.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface ImplementationClass {
+
+    /**
+     * Class, which will be used as implementation for annotated member.
+     *
+     * @return Serializaer to use.
+     */
+    Class<?> value();
+}
diff --git a/src/main/java/org/eclipse/yasson/JsonBindingProvider.java b/src/main/java/org/eclipse/yasson/JsonBindingProvider.java
new file mode 100644
index 0000000..3e4f4d4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/JsonBindingProvider.java
@@ -0,0 +1,29 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.spi.JsonbProvider;
+
+import org.eclipse.yasson.internal.JsonBindingBuilder;
+
+/**
+ * JsonbProvider implementation.
+ */
+public class JsonBindingProvider extends JsonbProvider {
+
+    @Override
+    public JsonbBuilder create() {
+        return new JsonBindingBuilder();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/YassonConfig.java b/src/main/java/org/eclipse/yasson/YassonConfig.java
new file mode 100644
index 0000000..9387eea
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/YassonConfig.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2019, 2022 IBM 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import java.util.Map;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.serializer.JsonbSerializer;
+
+/**
+ * Custom properties for configuring Yasson outside of the specification {@link jakarta.json.bind.JsonbConfig} scope.
+ */
+public class YassonConfig extends JsonbConfig {
+    
+    /**
+     * @see #withFailOnUnknownProperties(boolean)
+     */
+    public static final String FAIL_ON_UNKNOWN_PROPERTIES = "jsonb.fail-on-unknown-properties";
+
+    /**
+     * @see #withUserTypeMapping(java.util.Map)
+     */
+    public static final String USER_TYPE_MAPPING = "jsonb.user-type-mapping";
+    
+    /**
+     * @see #withZeroTimeParseDefaulting(boolean)
+     */
+    public static final String ZERO_TIME_PARSE_DEFAULTING = "jsonb.zero-time-defaulting";
+
+    /**
+     * @see #withNullRootSerializer(jakarta.json.bind.serializer.JsonbSerializer)
+     */
+    public static final String NULL_ROOT_SERIALIZER = "yasson.null-root-serializer";
+
+    /**
+     * @see #withEagerParsing(Class...)
+     */
+    public static final String EAGER_PARSE_CLASSES = "yasson.eager-parse-classes";
+
+    /**
+     * @see #withForceMapArraySerializerForNullKeys(boolean)
+     */
+    public static final String FORCE_MAP_ARRAY_SERIALIZER_FOR_NULL_KEYS = "yasson.force-map-array-serializer-for-null-keys";
+
+    /**
+     * @see #withTimeInMillisAsAString(boolean)
+     */
+    public static final String DATE_TIME_IN_MILLIS_AS_A_STRING = "yasson.time-in-millis-as-a-string";
+
+    /**
+     * Property used to specify behaviour on deserialization when JSON document contains properties
+     * which doesn't exist in the target class. Default value is 'false'.
+     * @param failOnUnknownProperties Whether or not to fail if unknown properties are encountered
+     * @return This YassonConfig instance
+     */
+    public YassonConfig withFailOnUnknownProperties(boolean failOnUnknownProperties) {
+        setProperty(FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties);
+        return this;
+    }
+    
+    /**
+     * User type mapping for map interface to implementation classes.
+     * @param mapping A map of interface to implementation class mappings
+     * @return This YassonConfig instance
+     */
+    public YassonConfig withUserTypeMapping(Map<Class<?>, Class<?>> mapping) {
+        setProperty(USER_TYPE_MAPPING, mapping);
+        return this;
+    }
+    
+    /**
+     * <p>Makes parsing dates defaulting to zero hour, minute and second.
+     * This will made available to parse patterns like yyyy.MM.dd to
+     * {@link java.util.Date}, {@link java.util.Calendar}, {@link java.time.Instant} {@link java.time.LocalDate}
+     * or even {@link java.time.ZonedDateTime}.
+     * <p>If time zone is not set in the pattern then UTC time zone is used.
+     * So for example json value 2018.01.01 becomes 2018.01.01 00:00:00 UTC when parsed
+     * to instant {@link java.time.Instant} or {@link java.time.ZonedDateTime}.
+     * @param defaultZeroHour Whether or not to default parsing dates to the zero hour
+     * @return This YassonConfig instance
+     */
+    public YassonConfig withZeroTimeParseDefaulting(boolean defaultZeroHour) {
+        setProperty(ZERO_TIME_PARSE_DEFAULTING, defaultZeroHour);
+        return this;
+    }
+    
+    /**
+     * Serializer to use when object provided to {@link jakarta.json.bind.Jsonb#toJson(Object)} is {@code null} or an empty
+     * Optional. Must be instance of {@link jakarta.json.bind.serializer.JsonbSerializer}{@code <Object>}. Its obj value
+     * will be respective parameter.
+     * @param nullSerializer JsonbSerializer instance to use for serializing null root values
+     * @return This YassonConfig instance
+     */
+    public YassonConfig withNullRootSerializer(JsonbSerializer<?> nullSerializer) {
+        setProperty(NULL_ROOT_SERIALIZER, nullSerializer);
+        return this;
+    }
+    
+    /**
+     * @param classes A list of classes to eagerly parse upon creation of the Jsonb instance used with this configuration. 
+     * @return This YassonConfig instance
+     */
+    public YassonConfig withEagerParsing(Class<?>... classes) {
+        setProperty(EAGER_PARSE_CLASSES, classes);
+        return this;
+    }
+
+    /**
+     * Property needed to make MapToEntriesArraySerializer the serializer used
+     * when a null key is found in the map. Default value is false.
+     * @param value true to force the use of the MapToEntriesArraySerializer if
+     *              a null key is detected in the map, false to work as before
+     * @return This YassonConfig instance
+     */
+    public YassonConfig withForceMapArraySerializerForNullKeys(boolean value) {
+        setProperty(FORCE_MAP_ARRAY_SERIALIZER_FOR_NULL_KEYS, value);
+        return this;
+    }
+
+    /**
+     * It is required to handle time millisecond format as a number. See
+     * {@link jakarta.json.bind.annotation.JsonbDateFormat#TIME_IN_MILLIS}. It is possible to override this and force
+     * Yasson to handle it as a String, by using this method.
+     *
+     * @param value whether to treat dates formatted by {@link jakarta.json.bind.annotation.JsonbDateFormat#TIME_IN_MILLIS}
+     *              as a String. Default value is {@code false}.
+     * @return This YassonConfig instance
+     * @since 3.0.0
+     */
+    public YassonConfig withTimeInMillisAsAString(boolean value) {
+        setProperty(DATE_TIME_IN_MILLIS_AS_A_STRING, value);
+        return this;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/YassonJsonb.java b/src/main/java/org/eclipse/yasson/YassonJsonb.java
new file mode 100644
index 0000000..f484300
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/YassonJsonb.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.JsonStructure;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+
+/**
+ * Adds methods to Jsonb that are operating directly with {@link JsonGenerator} or {@link JsonParser} types.
+ * <p>
+ * {@link jakarta.json.spi.JsonProvider} operates on top of the
+ * {@link java.io.InputStream} / {@link java.io.OutputStream} or {@link java.io.Reader} / {@link java.io.Writer}
+ * and creates generator / parser instances during runtime.
+ * </p>
+ * <p>
+ * This interface accepts instantiated generators and parsers with different input / output sources.
+ * </p>
+ */
+public interface YassonJsonb extends jakarta.json.bind.Jsonb {
+
+    /**
+     * Reads in a JSON data with a specified {@link JsonParser} and return the
+     * resulting content tree. Provided json parser must be fully initialized,
+     * no further configurations will be applied.
+     *
+     * @param jsonParser The json parser instance to be used to read JSON data.
+     * @param type       Type of the content tree's root object.
+     * @param <T>        Type of the content tree's root object.
+     * @return the newly created root object of the java content tree
+     * @throws JsonbException       If any unexpected error(s) occur(s) during deserialization.
+     */
+    <T> T fromJson(JsonParser jsonParser, Class<T> type) throws JsonbException;
+
+    /**
+     * Reads in a JSON data with a specified {@link JsonParser} and return the
+     * resulting content tree. Provided json parser must be fully initialized,
+     * no further configurations will be applied.
+     *
+     * @param jsonParser  The json parser instance to be used to read JSON data.
+     * @param runtimeType Runtime type of the content tree's root object.
+     * @param <T>         Type of the content tree's root object.
+     * @return the newly created root object of the java content tree
+     * @throws JsonbException       If any unexpected error(s) occur(s) during deserialization.
+     */
+    <T> T fromJson(JsonParser jsonParser, Type runtimeType) throws JsonbException;
+
+    /**
+     * Reads a {@link JsonStructure} and and converts it into
+     * resulting java content tree.
+     *
+     * @param jsonStructure {@link JsonStructure} to be used as a source for conversion.
+     * @param type          Type of the content tree's root object.
+     * @param <T>           Type of the content tree's root object.
+     * @return the newly created root object of the java content tree
+     * @throws JsonbException       If any unexpected error(s) occur(s) during conversion.
+     */
+    <T> T fromJsonStructure(JsonStructure jsonStructure, Class<T> type) throws JsonbException;
+
+    /**
+     * Reads a {@link JsonStructure} and and converts it into
+     * resulting java content tree.
+     *
+     * @param jsonStructure {@link JsonStructure} to be used as a source for conversion.
+     * @param runtimeType   Runtime type of the content tree's root object.
+     * @param <T>           Type of the content tree's root object.
+     * @return the newly created root object of the java content tree
+     * @throws JsonbException       If any unexpected error(s) occur(s) during deserialization.
+     */
+    <T> T fromJsonStructure(JsonStructure jsonStructure, Type runtimeType) throws JsonbException;
+
+    /**
+     * Writes the object content tree with a specified {@link JsonGenerator}.
+     * Provided json generator must be fully initialized, no further configurations are applied.
+     *
+     * @param object        The object content tree to be serialized.
+     * @param jsonGenerator The json generator to write JSON data. The generator is not closed
+     *                      on a completion for further interaction.
+     * @throws JsonbException       If any unexpected problem occurs during the
+     *                              serialization.
+     * @since JSON Binding 1.0
+     */
+    void toJson(Object object, JsonGenerator jsonGenerator) throws JsonbException;
+
+    /**
+     * Writes the object content tree with a specified {@link JsonGenerator}.
+     * Provided json generator must be fully initialized, no further configurations are applied.
+     *
+     * @param object        The object content tree to be serialized.
+     * @param runtimeType   Runtime type of the content tree's root object.
+     * @param jsonGenerator The json generator to write JSON data. The generator is not closed
+     *                      on a completion for further interaction.
+     * @throws JsonbException       If any unexpected problem occurs during the
+     *                              serialization.
+     * @since JSON Binding 1.0
+     */
+    void toJson(Object object, Type runtimeType, JsonGenerator jsonGenerator) throws JsonbException;
+
+    /**
+     * Serializes the object content tree to a {@link jakarta.json.JsonStructure}.
+     *
+     * @param object The object content tree to be serialized.
+     * @return The {@link JsonStructure} serialized from java content tree.
+     * @throws JsonbException       If any unexpected problem occurs during the
+     *                              serialization.
+     * @since JSON Binding 1.0
+     */
+    JsonStructure toJsonStructure(Object object) throws JsonbException;
+
+    /**
+     * Serializes the object content tree to a {@link jakarta.json.JsonStructure}.
+     *
+     * @param object      The object content tree to be serialized.
+     * @param runtimeType Runtime type of the content tree's root object.
+     * @return The {@link JsonStructure} serialized from java content tree.
+     * @throws JsonbException       If any unexpected problem occurs during the
+     *                              serialization.
+     * @since JSON Binding 1.0
+     */
+    JsonStructure toJsonStructure(Object object, Type runtimeType) throws JsonbException;
+}
diff --git a/src/main/java/org/eclipse/yasson/YassonProperties.java b/src/main/java/org/eclipse/yasson/YassonProperties.java
new file mode 100644
index 0000000..d1a9879
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/YassonProperties.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020 Payara Foundation 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+/**
+ * @deprecated Use {@link YassonConfig} instead
+ */
+@Deprecated
+public class YassonProperties {
+
+    private YassonProperties() {
+        throw new IllegalStateException("Util classes cannot be instantiated.");
+    }
+
+    /**
+     * @deprecated
+     * @see YassonConfig#withFailOnUnknownProperties(boolean)
+     */
+    public static final String FAIL_ON_UNKNOWN_PROPERTIES = YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES;
+
+    /**
+     * @deprecated
+     * @see YassonConfig#withUserTypeMapping(java.util.Map)
+     */
+    public static final String USER_TYPE_MAPPING = YassonConfig.USER_TYPE_MAPPING;
+    
+    /**
+     * @deprecated
+     * @see YassonConfig#withZeroTimeParseDefaulting(boolean)
+     */
+    public static final String ZERO_TIME_PARSE_DEFAULTING = YassonConfig.ZERO_TIME_PARSE_DEFAULTING;
+
+    /**
+     * @deprecated
+     * @see YassonConfig#withNullRootSerializer(jakarta.json.bind.serializer.JsonbSerializer)
+     */
+    public static final String NULL_ROOT_SERIALIZER = YassonConfig.NULL_ROOT_SERIALIZER;
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/AnnotationFinder.java b/src/main/java/org/eclipse/yasson/internal/AnnotationFinder.java
new file mode 100644
index 0000000..b299551
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/AnnotationFinder.java
@@ -0,0 +1,148 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Finds an annotation including inherited annotations (e.g. meta-annotations).
+ */
+class AnnotationFinder {
+
+    private static final String CONSTRUCTOR_PROPERTIES_ANNOTATION = "java.beans.ConstructorProperties";
+    private static final Logger LOGGER = Logger.getLogger(AnnotationFinder.class.getName());
+
+    private final String annotationClassName;
+    private final Class<? extends Annotation> annotationClass; // may be null
+
+    /**
+     * Gets the {@link AnnotationFinder} for the given Annotation-Type.
+     *
+     * @param annotation {@link Class}, that is a sub-type of {@link Annotation}
+     * @return {@link AnnotationFinder}
+     */
+    public static AnnotationFinder findAnnotation(Class<?> annotation) {
+        return findAnnotationByName(annotation.getName());
+    }
+
+    /**
+     * Gets the {@link AnnotationFinder} for the given Annotation-Type Name.
+     *
+     * @param annotationClassName {@link String}, that is a sub-type of {@link Annotation}
+     * @return {@link AnnotationFinder}
+     */
+    public static AnnotationFinder findAnnotationByName(String annotationClassName) {
+        return new AnnotationFinder(annotationClassName, getOptionalAnnotationClass(annotationClassName));
+    }
+
+    /**
+     * Gets the {@link AnnotationFinder} for @ConstructorProperties-Annotation.
+     *
+     * @return {@link AnnotationFinder}
+     */
+    public static AnnotationFinder findConstructorProperties() {
+        return findAnnotationByName(CONSTRUCTOR_PROPERTIES_ANNOTATION);
+    }
+
+    private AnnotationFinder(String annotationClassName, Class<? extends Annotation> annotationClass) {
+        this.annotationClassName = annotationClassName;
+        this.annotationClass = annotationClass;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T extends Annotation> T in(Annotation[] annotations) {
+        if (annotationClass == null) {
+            return null;
+        }
+        return (T) findAnnotation(annotations, annotationClass, new HashSet<>());
+    }
+
+    /**
+     * Looks for the annotation {@link #in(Annotation[])} <br>
+     * and executes the "value" Method of it dynamically.
+     *
+     * @param annotations - Array of {@link Annotation}n.
+     * @return {@link Object}
+     */
+    public Object valueIn(Annotation[] annotations) {
+        return invocateValueMethod(in(annotations));
+    }
+
+    private Object invocateValueMethod(Annotation annotation) {
+        if (annotation == null) {
+            return null;
+        }
+        try {
+            return annotation.annotationType().getMethod("value").invoke(annotation);
+        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            String message = Messages
+                    .getMessage(MessageKeys.MISSING_VALUE_PROPERTY_IN_ANNOTATION, annotation.annotationType().getName());
+            LOGGER.finest(message);
+            return null;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T extends Annotation> Class<T> getOptionalAnnotationClass(String classname) {
+        try {
+            return (Class<T>) Class.forName(classname);
+        } catch (ClassNotFoundException e) {
+            String message = Messages.getMessage(MessageKeys.ANNOTATION_NOT_AVAILABLE, classname);
+            LOGGER.finest(message);
+            return null;
+        }
+    }
+
+    /**
+     * Searches for annotation, collects processed, to avoid StackOverflow.
+     */
+    // "static" to use it in a hybrid procedural and object oriented manner.
+    @SuppressWarnings("unchecked")
+    public static <T extends Annotation> T findAnnotation(Annotation[] declaredAnnotations,
+                                                          Class<T> annotationClass,
+                                                          Set<Annotation> processed) {
+        for (Annotation candidate : declaredAnnotations) {
+            final Class<? extends Annotation> annType = candidate.annotationType();
+            if (annType.equals(annotationClass)) {
+                return (T) candidate;
+            }
+            processed.add(candidate);
+            final List<Annotation> inheritedAnnotations = new ArrayList<>(Arrays.asList(annType.getDeclaredAnnotations()));
+            inheritedAnnotations.removeAll(processed);
+            if (inheritedAnnotations.size() > 0) {
+                final T inherited = findAnnotation(inheritedAnnotations.toArray(new Annotation[inheritedAnnotations.size()]),
+                                                   annotationClass,
+                                                   processed);
+                if (inherited != null) {
+                    return inherited;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        return "AnnotationFinder [annotationClassName=" + annotationClassName + ", annotationClass=" + annotationClass + "]";
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java b/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java
new file mode 100644
index 0000000..36c2943
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/AnnotationIntrospector.java
@@ -0,0 +1,988 @@
+/*
+ * Copyright (c) 2016, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Parameter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
+import java.time.temporal.TemporalAccessor;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.Queue;
+import java.util.Set;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.bind.annotation.JsonbNillable;
+import jakarta.json.bind.annotation.JsonbNumberFormat;
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import jakarta.json.bind.annotation.JsonbSubtype;
+import jakarta.json.bind.annotation.JsonbTransient;
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeInfo;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+import jakarta.json.bind.annotation.JsonbVisibility;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+
+import org.eclipse.yasson.ImplementationClass;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.components.DeserializerBinding;
+import org.eclipse.yasson.internal.components.SerializerBinding;
+import org.eclipse.yasson.internal.model.AnnotationTarget;
+import org.eclipse.yasson.internal.model.CreatorModel;
+import org.eclipse.yasson.internal.model.JsonbAnnotatedElement;
+import org.eclipse.yasson.internal.model.JsonbAnnotatedElement.AnnotationWrapper;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.model.Property;
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+import org.eclipse.yasson.internal.model.customization.TypeInheritanceConfiguration;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Introspects configuration on classes and their properties by reading annotations.
+ */
+public class AnnotationIntrospector {
+
+    //    private static final Set<Class<?>> OPTIONALS = Set.of(Optional.class,
+    //                                                          OptionalInt.class,
+    //                                                          OptionalLong.class,
+    //                                                          OptionalDouble.class);
+
+    private final JsonbContext jsonbContext;
+    private final ConstructorPropertiesAnnotationIntrospector constructorPropertiesIntrospector;
+
+    private static final Set<Class<? extends Annotation>> REPEATABLE = Set.of(JsonbTypeInfo.class);
+
+    /**
+     * Annotations to report exception when used in combination with {@link JsonbTransient}.
+     */
+    private static final List<Class<? extends Annotation>> TRANSIENT_INCOMPATIBLE =
+            Arrays.asList(JsonbDateFormat.class, JsonbNumberFormat.class, JsonbProperty.class,
+                          JsonbTypeAdapter.class, JsonbTypeSerializer.class, JsonbTypeDeserializer.class);
+
+    /**
+     * Creates annotation introspecting component passing {@link JsonbContext} inside.
+     *
+     * @param jsonbContext mandatory
+     */
+    public AnnotationIntrospector(JsonbContext jsonbContext) {
+        Objects.requireNonNull(jsonbContext);
+        this.jsonbContext = jsonbContext;
+        this.constructorPropertiesIntrospector = ConstructorPropertiesAnnotationIntrospector.forContext(jsonbContext);
+    }
+
+    /**
+     * Gets a name of property for JSON marshalling.
+     * Can be different writeName for same property.
+     *
+     * @param property property representation - field, getter, setter (not null)
+     * @return read name
+     */
+    public String getJsonbPropertyJsonWriteName(Property property) {
+        Objects.requireNonNull(property);
+        return getJsonbPropertyCustomizedName(property, property.getGetterElement());
+    }
+
+    /**
+     * Gets a name of property for JSON unmarshalling.
+     * Can be different from writeName for same property.
+     *
+     * @param property property representation - field, getter, setter (not null)
+     * @return write name
+     */
+    public String getJsonbPropertyJsonReadName(Property property) {
+        Objects.requireNonNull(property);
+        return getJsonbPropertyCustomizedName(property, property.getSetterElement());
+    }
+
+    private String getJsonbPropertyCustomizedName(Property property, JsonbAnnotatedElement<Method> methodElement) {
+        JsonbProperty methodAnnotation = getMethodAnnotation(JsonbProperty.class, methodElement);
+        if (methodAnnotation != null && !methodAnnotation.value().isEmpty()) {
+            return methodAnnotation.value();
+        }
+        //in case of property name getter/setter override field value
+        JsonbProperty fieldAnnotation = getFieldAnnotation(JsonbProperty.class, property.getFieldElement());
+        if (fieldAnnotation != null && !fieldAnnotation.value().isEmpty()) {
+            return fieldAnnotation.value();
+        }
+
+        return null;
+    }
+
+    /**
+     * Searches for JsonbCreator annotation on constructors and static methods.
+     *
+     * @param clazz                  class to search
+     * @param propertyNamingStrategy The naming strategy to use for the ${@code JsonbConstructor} annotation,
+     *                               if set and no {@code JsonbProperty} annotations are present.
+     * @return JsonbCreator metadata object
+     */
+    public JsonbCreator getCreator(Class<?> clazz, PropertyNamingStrategy propertyNamingStrategy) {
+        JsonbCreator jsonbCreator = null;
+        Constructor<?>[] declaredConstructors =
+                AccessController.doPrivileged((PrivilegedAction<Constructor<?>[]>) clazz::getDeclaredConstructors);
+
+        for (Constructor<?> constructor : declaredConstructors) {
+            final jakarta.json.bind.annotation.JsonbCreator annot = findAnnotation(constructor.getDeclaredAnnotations(),
+                                                                                   jakarta.json.bind.annotation.JsonbCreator.class);
+            if (annot != null) {
+                jsonbCreator = createJsonbCreator(constructor, jsonbCreator, clazz, propertyNamingStrategy);
+            }
+        }
+
+        Method[] declaredMethods =
+                AccessController.doPrivileged((PrivilegedAction<Method[]>) clazz::getDeclaredMethods);
+        for (Method method : declaredMethods) {
+            final jakarta.json.bind.annotation.JsonbCreator annot = findAnnotation(method.getDeclaredAnnotations(),
+                                                                                   jakarta.json.bind.annotation.JsonbCreator.class);
+            if (annot != null && Modifier.isStatic(method.getModifiers())) {
+                if (!clazz.equals(method.getReturnType())) {
+                    throw new JsonbException(Messages.getMessage(MessageKeys.INCOMPATIBLE_FACTORY_CREATOR_RETURN_TYPE,
+                                                                 method,
+                                                                 clazz));
+                }
+                jsonbCreator = createJsonbCreator(method, jsonbCreator, clazz, propertyNamingStrategy);
+            }
+        }
+        if (jsonbCreator == null) {
+            jsonbCreator = ClassMultiReleaseExtension.findCreator(clazz, declaredConstructors, this, propertyNamingStrategy);
+            if (jsonbCreator == null) {
+                jsonbCreator = constructorPropertiesIntrospector.getCreator(declaredConstructors);
+            }
+        }
+        return jsonbCreator;
+    }
+
+    JsonbCreator createJsonbCreator(Executable executable, JsonbCreator existing, Class<?> clazz, PropertyNamingStrategy propertyNamingStrategy) {
+        if (existing != null) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.MULTIPLE_JSONB_CREATORS, clazz));
+        }
+
+        final Parameter[] parameters = executable.getParameters();
+
+        CreatorModel[] creatorModels = new CreatorModel[parameters.length];
+        for (int i = 0; i < parameters.length; i++) {
+            final Parameter parameter = parameters[i];
+            final JsonbProperty jsonbPropertyAnnotation = parameter.getAnnotation(JsonbProperty.class);
+            if (jsonbPropertyAnnotation != null && !jsonbPropertyAnnotation.value().isEmpty()) {
+                creatorModels[i] = new CreatorModel(jsonbPropertyAnnotation.value(), parameter, executable, jsonbContext);
+            } else {
+                final String translatedParameterName = propertyNamingStrategy.translateName(parameter.getName());
+                creatorModels[i] = new CreatorModel(translatedParameterName, parameter, executable, jsonbContext);
+            }
+        }
+
+        return new JsonbCreator(executable, creatorModels);
+    }
+
+    /**
+     * Checks for {@link JsonbAdapter} on a property.
+     *
+     * @param property property not null
+     * @return components info
+     */
+    public AdapterBinding getAdapterBinding(Property property) {
+        Objects.requireNonNull(property);
+        JsonbTypeAdapter adapterAnnotation = getAnnotationFromProperty(JsonbTypeAdapter.class, property)
+                .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeAdapter.class));
+        if (adapterAnnotation == null) {
+            return null;
+        }
+
+        return getAdapterBindingFromAnnotation(adapterAnnotation, ReflectionUtils.getOptionalRawType(property.getPropertyType()));
+    }
+
+    /**
+     * Checks for {@link JsonbAdapter} on a type.
+     *
+     * @param clsElement type not null
+     * @return components info
+     */
+    public AdapterBinding getAdapterBinding(JsonbAnnotatedElement<Class<?>> clsElement) {
+        Objects.requireNonNull(clsElement);
+
+        JsonbTypeAdapter adapterAnnotation = clsElement.getElement().getAnnotation(JsonbTypeAdapter.class);
+        if (adapterAnnotation == null) {
+            return null;
+        }
+
+        return getAdapterBindingFromAnnotation(adapterAnnotation, Optional.ofNullable(clsElement.getElement()));
+    }
+
+    private AdapterBinding getAdapterBindingFromAnnotation(JsonbTypeAdapter adapterAnnotation, Optional<Class<?>> expectedClass) {
+        final Class<? extends JsonbAdapter> adapterClass = adapterAnnotation.value();
+        final AdapterBinding adapterBinding = jsonbContext.getComponentMatcher().introspectAdapterBinding(adapterClass, null);
+
+        if (expectedClass.isPresent() && !(
+                ReflectionUtils.getRawType(adapterBinding.getBindingType()).isAssignableFrom(expectedClass.get()))) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_INCOMPATIBLE,
+                                                         adapterBinding.getBindingType(),
+                                                         expectedClass.get()));
+        }
+        return adapterBinding;
+    }
+
+    /**
+     * Checks for {@link JsonbDeserializer} on a property.
+     *
+     * @param property property not null
+     * @return components info
+     */
+    public DeserializerBinding getDeserializerBinding(Property property) {
+        Objects.requireNonNull(property);
+        JsonbTypeDeserializer deserializerAnnotation = getAnnotationFromProperty(JsonbTypeDeserializer.class, property)
+                .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeDeserializer.class));
+        if (deserializerAnnotation == null) {
+            return null;
+        }
+
+        final Class<? extends JsonbDeserializer> deserializerClass = deserializerAnnotation.value();
+        return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null);
+    }
+
+    /**
+     * Checks for {@link JsonbDeserializer} on a {@link Parameter}.
+     *
+     * @param parameter parameter not null
+     * @return components info
+     */
+    public DeserializerBinding<?> getDeserializerBinding(Parameter parameter) {
+        Objects.requireNonNull(parameter);
+        JsonbTypeDeserializer deserializerAnnotation =
+                Optional.ofNullable(parameter.getDeclaredAnnotation(JsonbTypeDeserializer.class))
+                        .orElseGet(() -> getAnnotationFromParameterType(parameter, JsonbTypeDeserializer.class));
+        if (deserializerAnnotation == null) {
+            return null;
+        }
+
+        final Class<? extends JsonbDeserializer> deserializerClass = deserializerAnnotation.value();
+        return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null);
+    }
+
+    /**
+     * Checks for {@link JsonbAdapter} on a {@link Parameter}.
+     *
+     * @param parameter parameter not null
+     * @return components info
+     */
+    public AdapterBinding getAdapterBinding(Parameter parameter) {
+        Objects.requireNonNull(parameter);
+        JsonbTypeAdapter adapter =
+                Optional.ofNullable(parameter.getDeclaredAnnotation(JsonbTypeAdapter.class))
+                        .orElseGet(() -> getAnnotationFromParameterType(parameter, JsonbTypeAdapter.class));
+        if (adapter == null) {
+            return null;
+        }
+
+        return getAdapterBindingFromAnnotation(adapter, ReflectionUtils.getOptionalRawType(parameter.getParameterizedType()));
+    }
+
+    private <T extends Annotation> T getAnnotationFromParameterType(Parameter parameter, Class<T> annotationClass) {
+        final Optional<Class<?>> optionalRawType = ReflectionUtils.getOptionalRawType(parameter.getParameterizedType());
+        //will not work for type variable properties, which are bound to class that is annotated.
+        return optionalRawType.map(aClass -> findAnnotation(collectAnnotations(aClass).getAnnotations(), annotationClass))
+                .orElse(null);
+    }
+
+    /**
+     * Checks for {@link JsonbDeserializer} on a type.
+     *
+     * @param clsElement type not null
+     * @return components info
+     */
+    public DeserializerBinding getDeserializerBinding(JsonbAnnotatedElement<Class<?>> clsElement) {
+        Objects.requireNonNull(clsElement);
+        JsonbTypeDeserializer deserializerAnnotation = clsElement.getElement().getAnnotation(JsonbTypeDeserializer.class);
+        if (deserializerAnnotation == null) {
+            return null;
+        }
+
+        final Class<? extends JsonbDeserializer> deserializerClass = deserializerAnnotation.value();
+        return jsonbContext.getComponentMatcher().introspectDeserializerBinding(deserializerClass, null);
+    }
+
+    /**
+     * Checks for {@link JsonbSerializer} on a property.
+     *
+     * @param property property not null
+     * @return components info
+     */
+    public SerializerBinding getSerializerBinding(Property property) {
+        Objects.requireNonNull(property);
+        JsonbTypeSerializer serializerAnnotation = getAnnotationFromProperty(JsonbTypeSerializer.class, property)
+                .orElseGet(() -> getAnnotationFromPropertyType(property, JsonbTypeSerializer.class));
+        if (serializerAnnotation == null) {
+            return null;
+        }
+
+        final Class<? extends JsonbSerializer> serializerClass = serializerAnnotation.value();
+        return jsonbContext.getComponentMatcher().introspectSerializerBinding(serializerClass, null);
+
+    }
+
+    /**
+     * Checks for {@link JsonbSerializer} on a type.
+     *
+     * @param clsElement type not null
+     * @return components info
+     */
+    public SerializerBinding getSerializerBinding(JsonbAnnotatedElement<Class<?>> clsElement) {
+        Objects.requireNonNull(clsElement);
+        JsonbTypeSerializer serializerAnnotation = clsElement.getElement().getAnnotation(JsonbTypeSerializer.class);
+        if (serializerAnnotation == null) {
+            return null;
+        }
+
+        final Class<? extends JsonbSerializer> serializerClass = serializerAnnotation.value();
+        return jsonbContext.getComponentMatcher().introspectSerializerBinding(serializerClass, null);
+    }
+
+    private <T extends Annotation> T getAnnotationFromPropertyType(Property property, Class<T> annotationClass) {
+        final Optional<Class<?>> optionalRawType = ReflectionUtils.getOptionalRawType(property.getPropertyType());
+        if (!optionalRawType.isPresent()) {
+            //will not work for type variable properties, which are bound to class that is annotated.
+            return null;
+        }
+        return findAnnotation(collectAnnotations(optionalRawType.get()).getAnnotations(), annotationClass);
+    }
+
+    /**
+     * Checks if property is nillable.
+     * Looks for {@link JsonbProperty} nillable attribute only.
+     * JsonbNillable is checked only for ClassModels.
+     *
+     * @param property property to search in, not null
+     * @return True if property should be serialized when null.
+     */
+    public Optional<Boolean> isPropertyNillable(Property property) {
+        Objects.requireNonNull(property);
+
+        Optional<JsonbNillable> nillable = getAnnotationFromProperty(JsonbNillable.class, property);
+        if (nillable.isPresent()) {
+            return nillable.map(JsonbNillable::value);
+        }
+        final Optional<JsonbProperty> jsonbProperty = getAnnotationFromProperty(JsonbProperty.class, property);
+        return jsonbProperty.map(JsonbProperty::nillable);
+
+    }
+
+    /**
+     * Checks for JsonbNillable annotation on a class, its superclasses and interfaces.
+     *
+     * @param clazzElement class to search JsonbNillable in.
+     * @return true if found
+     */
+    public boolean isClassNillable(JsonbAnnotatedElement<Class<?>> clazzElement) {
+        final JsonbNillable jsonbNillable = findAnnotation(clazzElement.getAnnotations(), JsonbNillable.class);
+        if (jsonbNillable != null) {
+            return jsonbNillable.value();
+        }
+        Class<?> clazz = clazzElement.getElement();
+        if (clazz == Optional.class
+                || clazz == OptionalDouble.class
+                || clazz == OptionalInt.class
+                || clazz == OptionalLong.class) {
+            return true;
+        }
+        return jsonbContext.getConfigProperties().getConfigNullable();
+    }
+
+    /**
+     * Checks for {@link JsonbPropertyOrder} annotation.
+     *
+     * @param clazzElement class to search on
+     * @return ordered properties names or null if not found
+     */
+    public String[] getPropertyOrder(JsonbAnnotatedElement<Class<?>> clazzElement) {
+        final JsonbPropertyOrder jsonbPropertyOrder = clazzElement.getElement().getAnnotation(JsonbPropertyOrder.class);
+        return jsonbPropertyOrder != null ? jsonbPropertyOrder.value() : null;
+    }
+
+    /**
+     * Checks if property is annotated transient. If JsonbTransient annotation is present on field getter or setter, and other
+     * annotation is present
+     * on either of it, JsonbException is thrown with message describing collision.
+     *
+     * @param property The property to inspect if there is any {@link JsonbTransient} annotation defined for it
+     * @return Set of {@link AnnotationTarget}s specifying in which scope the {@link JsonbTransient} is applied
+     */
+    public EnumSet<AnnotationTarget> getJsonbTransientCategorized(Property property) {
+        Objects.requireNonNull(property);
+        EnumSet<AnnotationTarget> transientTarget = EnumSet.noneOf(AnnotationTarget.class);
+        Map<AnnotationTarget, JsonbTransient> annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized(
+                JsonbTransient.class,
+                property);
+        if (annotationFromPropertyCategorized.size() > 0) {
+            transientTarget.addAll(annotationFromPropertyCategorized.keySet());
+            return transientTarget;
+        }
+
+        return transientTarget;
+    }
+
+    /**
+     * Search {@link JsonbDateFormat} on property, if not found looks at annotations declared on property type class.
+     *
+     * @param property Property to search on.
+     * @return Map of {@link JsonbDateFormatter} instances categorized by their scopes (class, property, getter or setter). If
+     * there is no date
+     * formatter specified for given property, an empty map would be returned
+     */
+    public Map<AnnotationTarget, JsonbDateFormatter> getJsonbDateFormatCategorized(Property property) {
+        Objects.requireNonNull(property);
+
+        Map<AnnotationTarget, JsonbDateFormatter> result = new HashMap<>();
+        Map<AnnotationTarget, JsonbDateFormat> annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized(
+                JsonbDateFormat.class,
+                property);
+        if (annotationFromPropertyCategorized.size() != 0) {
+            annotationFromPropertyCategorized.forEach((key, annotation) -> result
+                    .put(key, createJsonbDateFormatter(annotation.value(), annotation.locale(), property)));
+        }
+
+        // No date format on property, try class level
+        // if property is not TypeVariable and its class is not date skip it
+        final Optional<Class<?>> propertyRawTypeOptional = ReflectionUtils.getOptionalRawType(property.getPropertyType());
+        if (propertyRawTypeOptional.isPresent()) {
+            Class<?> rawType = propertyRawTypeOptional.get();
+            if (!(
+                    Date.class.isAssignableFrom(rawType) || Calendar.class.isAssignableFrom(rawType)
+                            || TemporalAccessor.class.isAssignableFrom(rawType))) {
+                return new HashMap<>();
+            }
+        }
+
+        JsonbDateFormat classLevelDateFormatter = findAnnotation(property.getDeclaringClassElement().getAnnotations(),
+                                                                 JsonbDateFormat.class);
+        if (classLevelDateFormatter != null) {
+            result.put(AnnotationTarget.CLASS,
+                       createJsonbDateFormatter(classLevelDateFormatter.value(), classLevelDateFormatter.locale(), property));
+        }
+
+        return result;
+    }
+
+    /**
+     * Search for {@link JsonbDateFormat} annotation on java class and construct {@link JsonbDateFormatter}.
+     * If not found looks at annotations declared on property type class.
+     *
+     * @param clazzElement class to search not null
+     * @return formatter to use
+     */
+    public JsonbDateFormatter getJsonbDateFormat(JsonbAnnotatedElement<Class<?>> clazzElement) {
+        Objects.requireNonNull(clazzElement);
+        final JsonbDateFormat format = findAnnotation(clazzElement.getAnnotations(), JsonbDateFormat.class);
+        if (format == null) {
+            return jsonbContext.getConfigProperties().getConfigDateFormatter();
+        }
+        return new JsonbDateFormatter(format.value(), format.locale());
+    }
+
+    /**
+     * Search for {@link JsonbNumberFormat} annotation on java class.
+     *
+     * @param clazzElement class to search not null
+     * @return formatter to use
+     */
+    public JsonbNumberFormatter getJsonbNumberFormat(JsonbAnnotatedElement<Class<?>> clazzElement) {
+        final JsonbNumberFormat formatAnnotation = findAnnotation(clazzElement.getAnnotations(), JsonbNumberFormat.class);
+        if (formatAnnotation == null) {
+            return null;
+        }
+        return new JsonbNumberFormatter(formatAnnotation.value(), formatAnnotation.locale());
+    }
+
+    /**
+     * Search {@link JsonbNumberFormat} on property, if not found looks at annotations declared on property type class.
+     *
+     * @param property Property to search on.
+     * @return Map of {@link JsonbNumberFormatter} instances categorized by their scopes (class, property, getter or setter).
+     * If there is no number
+     * formatter specified for given property, an empty map would be returned
+     */
+    public Map<AnnotationTarget, JsonbNumberFormatter> getJsonNumberFormatter(Property property) {
+        Map<AnnotationTarget, JsonbNumberFormatter> result = new HashMap<>();
+        Map<AnnotationTarget, JsonbNumberFormat> annotationFromPropertyCategorized = getAnnotationFromPropertyCategorized(
+                JsonbNumberFormat.class,
+                property);
+        //        if (annotationFromPropertyCategorized.size() == 0) {
+        //            final Optional<Class<?>> propertyRawTypeOptional = ReflectionUtils.getOptionalRawType(property
+        //            .getPropertyType());
+        //            if (propertyRawTypeOptional.isPresent()) {
+        //                Class<?> rawType = propertyRawTypeOptional.get();
+        //                if (!Number.class.isAssignableFrom(rawType)) {
+        //                    return new HashMap<>();
+        //                }
+        //            }
+        //        } else {
+        //            annotationFromPropertyCategorized.forEach((key, annotation) -> result
+        //                    .put(key, new JsonbNumberFormatter(annotation.value(), annotation.locale())));
+        //        }
+        annotationFromPropertyCategorized.forEach((key, annotation) -> result
+                .put(key, new JsonbNumberFormatter(annotation.value(), annotation.locale())));
+
+        JsonbNumberFormat classLevelNumberFormatter = findAnnotation(property.getDeclaringClassElement().getAnnotations(),
+                                                                     JsonbNumberFormat.class);
+        if (classLevelNumberFormatter != null) {
+            result.put(AnnotationTarget.CLASS,
+                       new JsonbNumberFormatter(classLevelNumberFormatter.value(), classLevelNumberFormatter.locale()));
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns {@link JsonbNumberFormatter} instance if {@link JsonbNumberFormat} annotation is present.
+     *
+     * @param param annotated method parameter
+     * @return formatter instance if {@link JsonbNumberFormat} is present otherwise null
+     */
+    public JsonbNumberFormatter getConstructorNumberFormatter(JsonbAnnotatedElement<Parameter> param) {
+        return param.getAnnotation(JsonbNumberFormat.class)
+                .map(annotation -> new JsonbNumberFormatter(annotation.value(), annotation.locale()))
+                .orElse(null);
+    }
+
+    /**
+     * Returns {@link JsonbDateFormatter} instance if {@link JsonbDateFormat} annotation is present.
+     *
+     * @param param annotated method parameter
+     * @return formatter instance if {@link JsonbDateFormat} is present otherwise null
+     */
+    public JsonbDateFormatter getConstructorDateFormatter(JsonbAnnotatedElement<Parameter> param) {
+        return param.getAnnotation(JsonbDateFormat.class)
+                .map(annotation -> new JsonbDateFormatter(DateTimeFormatter.ofPattern(annotation.value(),
+                                                                                      Locale.forLanguageTag(annotation.locale())),
+                                                          annotation.value(), annotation.locale()))
+                .orElse(null);
+    }
+
+    /**
+     * Creates {@link JsonbDateFormatter} caches formatter instance if possible.
+     * For DEFAULT_FORMAT appropriate singleton instances from java.time.format.DateTimeFormatter
+     * are used in date converters.
+     */
+    private JsonbDateFormatter createJsonbDateFormatter(String format, String locale, Property property) {
+        if (JsonbDateFormat.TIME_IN_MILLIS.equals(format) || JsonbDateFormat.DEFAULT_FORMAT.equals(format)) {
+            //for epochMillis formatter is not used, for default format singleton instances of DateTimeFormatter
+            //are used in the converters
+            return new JsonbDateFormatter(format, locale);
+        }
+
+        final Optional<Class<?>> optionalRawType = ReflectionUtils.getOptionalRawType(property.getPropertyType());
+        final Class<?> propertyRawType = optionalRawType.orElse(null);
+
+        if (propertyRawType != null
+                && !TemporalAccessor.class.isAssignableFrom(propertyRawType)
+                && !Date.class.isAssignableFrom(propertyRawType)
+                && !Calendar.class.isAssignableFrom(propertyRawType)) {
+            throw new IllegalStateException(Messages.getMessage(MessageKeys.UNSUPPORTED_DATE_TYPE, propertyRawType));
+        }
+
+        DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
+        builder.appendPattern(format);
+        if (jsonbContext.getConfigProperties().isZeroTimeDefaulting()) {
+            builder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
+            builder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0);
+            builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
+        }
+        DateTimeFormatter dateTimeFormatter = builder.toFormatter(Locale.forLanguageTag(locale));
+        return new JsonbDateFormatter(dateTimeFormatter, format, locale);
+    }
+
+    /**
+     * Get a @JsonbVisibility annotation from a class or its package.
+     *
+     * @param clazz Class to lookup annotation
+     * @return Instantiated PropertyVisibilityStrategy if annotation is present
+     */
+    public PropertyVisibilityStrategy getPropertyVisibilityStrategy(Class<?> clazz) {
+        JsonbVisibility visibilityAnnotation = findAnnotation(clazz.getDeclaredAnnotations(), JsonbVisibility.class);
+        if ((visibilityAnnotation == null) && (clazz.getPackage() != null)) {
+            visibilityAnnotation = findAnnotation(clazz.getPackage().getDeclaredAnnotations(), JsonbVisibility.class);
+        }
+        if (visibilityAnnotation != null) {
+            return ReflectionUtils.createNoArgConstructorInstance(
+                    ReflectionUtils.getDefaultConstructor(visibilityAnnotation.value(), true));
+        }
+        return jsonbContext.getConfigProperties().getPropertyVisibilityStrategy();
+    }
+
+    /**
+     * Gets an annotation from first resolved annotation in a property in this order:
+     * <p>1. Field, 2. Getter, 3 Setter.</p>
+     * First found overrides other.
+     *
+     * @param annotationClass Annotation class to search for
+     * @param property        property to search in
+     * @param <T>             Annotation type
+     * @return Annotation if found, null otherwise
+     */
+    private <T extends Annotation> Optional<T> getAnnotationFromProperty(Class<T> annotationClass, Property property) {
+        T fieldAnnotation = getFieldAnnotation(annotationClass, property.getFieldElement());
+        if (fieldAnnotation != null) {
+            return Optional.of(fieldAnnotation);
+        }
+
+        T getterAnnotation = getMethodAnnotation(annotationClass, property.getGetterElement());
+        if (getterAnnotation != null) {
+            return Optional.of(getterAnnotation);
+        }
+
+        T setterAnnotation = getMethodAnnotation(annotationClass, property.getSetterElement());
+        if (setterAnnotation != null) {
+            return Optional.of(setterAnnotation);
+        }
+
+        return Optional.empty();
+    }
+
+    /**
+     * An override of {@link #getAnnotationFromProperty(Class, Property)} in which it returns the results as a map so that the
+     * caller can decide which
+     * one to be used for read/write operation. Some annotations should have different behaviours based on the scope that
+     * they're applied on.
+     *
+     * @param annotationClass The annotation class to search
+     * @param property        The property to search in
+     * @param <T>             Annotation type
+     * @return A map of all occurrences of requested annotation for given property. Caller can determine based on
+     * {@link AnnotationTarget} that given
+     * annotation is specified on what level (Class, Property, Getter or Setter). If no annotation found for given property, an
+     * empty map would be
+     * returned
+     */
+    private <T extends Annotation> Map<AnnotationTarget, T> getAnnotationFromPropertyCategorized(Class<T> annotationClass,
+                                                                                                 Property property) {
+        Map<AnnotationTarget, T> result = new HashMap<>();
+        T fieldAnnotation = getFieldAnnotation(annotationClass, property.getFieldElement());
+        if (fieldAnnotation != null) {
+            result.put(AnnotationTarget.PROPERTY, fieldAnnotation);
+        }
+
+        T getterAnnotation = getMethodAnnotation(annotationClass, property.getGetterElement());
+        if (getterAnnotation != null) {
+            result.put(AnnotationTarget.GETTER, getterAnnotation);
+        }
+
+        T setterAnnotation = getMethodAnnotation(annotationClass, property.getSetterElement());
+        if (setterAnnotation != null) {
+            result.put(AnnotationTarget.SETTER, setterAnnotation);
+        }
+
+        return result;
+    }
+
+    private <T extends Annotation> T getFieldAnnotation(Class<T> annotationClass, JsonbAnnotatedElement<Field> fieldElement) {
+        if (fieldElement == null) {
+            return null;
+        }
+        return findAnnotation(fieldElement.getAnnotations(), annotationClass);
+    }
+
+    private <T extends Annotation> T findAnnotation(Annotation[] declaredAnnotations, Class<T> annotationClass) {
+        return AnnotationFinder.findAnnotation(declaredAnnotations, annotationClass, new HashSet<>());
+    }
+
+    /**
+     * Finds annotations incompatible with {@link JsonbTransient} annotation.
+     *
+     * @param target target to check
+     */
+    public void checkTransientIncompatible(JsonbAnnotatedElement<?> target) {
+        if (target == null) {
+            return;
+        }
+
+        for (Class<? extends Annotation> ann : TRANSIENT_INCOMPATIBLE) {
+            Annotation annotation = findAnnotation(target.getAnnotations(), ann);
+            if (annotation != null) {
+                throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_TRANSIENT_WITH_OTHER_ANNOTATIONS));
+            }
+        }
+    }
+
+    private <T extends Annotation> T getMethodAnnotation(Class<T> annotationClass, JsonbAnnotatedElement<Method> methodElement) {
+        if (methodElement == null) {
+            return null;
+        }
+        return findAnnotation(methodElement.getAnnotations(), annotationClass);
+    }
+
+    private <T extends Annotation> void collectFromInterfaces(Class<T> annotationClass,
+                                                              Class<?> clazz,
+                                                              Map<Class<?>, T> collectedAnnotations) {
+
+        for (Class<?> interfaceClass : clazz.getInterfaces()) {
+            T annotation = findAnnotation(interfaceClass.getDeclaredAnnotations(), annotationClass);
+            if (annotation != null) {
+                collectedAnnotations.put(interfaceClass, annotation);
+            }
+            collectFromInterfaces(annotationClass, interfaceClass, collectedAnnotations);
+        }
+    }
+
+    /**
+     * Get class interfaces recursively.
+     *
+     * @param cls Class to process.
+     * @return A list of all class interfaces.
+     */
+    public Set<Class<?>> collectInterfaces(Class<?> cls) {
+        Set<Class<?>> collected = new LinkedHashSet<>();
+        Queue<Class<?>> toScan = new LinkedList<>(Arrays.asList(cls.getInterfaces()));
+        Class<?> nextIfc;
+        while ((nextIfc = toScan.poll()) != null) {
+            collected.add(nextIfc);
+            toScan.addAll(Arrays.asList(nextIfc.getInterfaces()));
+        }
+        return collected;
+    }
+
+    /**
+     * Processes customizations.
+     *
+     * @param clsElement             Element to process.
+     * @param propertyNamingStrategy The naming strategy to use for the ${@code JsonbConstructor} annotation,
+     *                               if set and no {@code JsonbProperty} annotations are present.
+     * @return Populated {@link ClassCustomization} instance.
+     */
+    public ClassCustomization introspectCustomization(JsonbAnnotatedElement<Class<?>> clsElement,
+                                                      ClassCustomization parentCustomization,
+                                                      PropertyNamingStrategy propertyNamingStrategy) {
+        return ClassCustomization.builder()
+                .nillable(isClassNillable(clsElement))
+                .dateTimeFormatter(getJsonbDateFormat(clsElement))
+                .numberFormatter(getJsonbNumberFormat(clsElement))
+                .creator(getCreator(clsElement.getElement(), propertyNamingStrategy))
+                .propertyOrder(getPropertyOrder(clsElement))
+                .adapterBinding(getAdapterBinding(clsElement))
+                .serializerBinding(getSerializerBinding(clsElement))
+                .deserializerBinding(getDeserializerBinding(clsElement))
+                .propertyVisibilityStrategy(getPropertyVisibilityStrategy(clsElement.getElement()))
+                .polymorphismConfig(getPolymorphismConfig(clsElement, parentCustomization))
+                .build();
+    }
+
+    private TypeInheritanceConfiguration getPolymorphismConfig(JsonbAnnotatedElement<Class<?>> clsElement,
+                                                               ClassCustomization parentCustomization) {
+        TypeInheritanceConfiguration parentPolyConfig = parentCustomization.getPolymorphismConfig();
+
+        LinkedList<AnnotationWrapper<?>> annotations = clsElement.getAnnotations(JsonbTypeInfo.class);
+
+        if (parentPolyConfig != null) {
+            if (annotations.size() == 1 && annotations.getFirst().isInherited()) {
+                throw new JsonbException("Cannot process type information from multiple sources! Sources: "
+                                                 + parentPolyConfig.getDefinedType().getName() + " and "
+                                                 + annotations.getFirst());
+            } else if (annotations.size() > 1) {
+                throw new JsonbException("Cannot process type information from multiple sources! Sources: " + annotations);
+            } else if (annotations.isEmpty()) {
+                return TypeInheritanceConfiguration.builder().of(parentPolyConfig)
+                        .inherited(true)
+                        .build();
+            }
+        }
+        ListIterator<AnnotationWrapper<?>> listIterator = annotations.listIterator(annotations.size());
+        while (listIterator.hasPrevious()) {
+            AnnotationWrapper<?> annotationWrapper = listIterator.previous();
+            JsonbTypeInfo annotation = (JsonbTypeInfo) annotationWrapper.getAnnotation();
+            TypeInheritanceConfiguration.Builder builder = TypeInheritanceConfiguration.builder();
+            builder.fieldName(annotation.key())
+                    .inherited(annotationWrapper.isInherited())
+                    .parentConfig(parentPolyConfig)
+                    .definedType(annotationWrapper.getDefinedType());
+            for (JsonbSubtype subType : annotation.value()) {
+                if (!annotationWrapper.getDefinedType().isAssignableFrom(subType.type())) {
+                    throw new JsonbException("Defined alias type has to be child of the current type. JsonbSubType on the "
+                                                     + annotationWrapper.getDefinedType().getName()
+                                                     + " defines incorrect alias "
+                                                     + subType);
+                }
+                builder.alias(subType.type(), subType.alias());
+            }
+            parentPolyConfig = builder.build();
+        }
+
+        checkDuplicityPolymorphicPropertyNames(parentPolyConfig);
+
+        return parentPolyConfig;
+    }
+
+    private void checkDuplicityPolymorphicPropertyNames(TypeInheritanceConfiguration typeInheritanceConfiguration) {
+        if (typeInheritanceConfiguration == null) {
+            return;
+        }
+        Map<String, TypeInheritanceConfiguration> keyNames = new HashMap<>();
+        TypeInheritanceConfiguration current = typeInheritanceConfiguration;
+        while (current != null) {
+            String fieldName = current.getFieldName();
+            if (keyNames.containsKey(fieldName)) {
+                TypeInheritanceConfiguration conflicting = keyNames.get(fieldName);
+                throw new JsonbException("One polymorphic chain cannot have two conflicting property names. "
+                                                 + "Polymorphic type defined on the type "
+                                                 + conflicting.getDefinedType().getName() + " and "
+                                                 + current.getDefinedType().getName() + " have conflicting property name");
+            }
+            keyNames.put(fieldName, current);
+            current = current.getParentConfig();
+        }
+    }
+
+    /**
+     * Returns class if {@link ImplementationClass} annotation is present.
+     *
+     * @param property annotated property
+     * @return Class if {@link ImplementationClass} is present otherwise null
+     */
+    public Class<?> getImplementationClass(Property property) {
+        Optional<ImplementationClass> annotationFromProperty = getAnnotationFromProperty(ImplementationClass.class, property);
+        return annotationFromProperty.<Class<?>>map(ImplementationClass::value).orElse(null);
+    }
+
+    /**
+     * Collect annotations of given class, its interfaces and the package.
+     *
+     * @param clazz Class to process.
+     * @return Element with class and annotations.
+     */
+    public JsonbAnnotatedElement<Class<?>> collectAnnotations(Class<?> clazz) {
+        JsonbAnnotatedElement<Class<?>> classElement = new JsonbAnnotatedElement<>(clazz);
+
+        if (BuiltInTypes.isKnownType(clazz)) {
+            return classElement;
+        }
+
+        Map<Class<? extends Annotation>, LinkedList<AnnotationWrapper<?>>> interfaceAnnotations
+                = collectInterfaceAnnotations(clazz, clazz);
+        for (LinkedList<AnnotationWrapper<?>> wrappers : interfaceAnnotations.values()) {
+            for (AnnotationWrapper<?> wrapper : wrappers) {
+                if (classElement.getAnnotation(wrapper.getAnnotation().annotationType()).isEmpty()
+                        || REPEATABLE.contains(wrapper.getAnnotation().annotationType())) {
+                    classElement.putAnnotationWrapper(wrapper);
+                }
+            }
+        }
+
+        if (!clazz.isPrimitive() && !clazz.isArray() && (clazz.getPackage() != null)) {
+            addIfNotPresent(classElement, null, clazz.getPackage().getAnnotations());
+        }
+        return classElement;
+    }
+
+    private Map<Class<? extends Annotation>, LinkedList<AnnotationWrapper<?>>> collectInterfaceAnnotations(Class<?> currentInterf,
+                                                                                                           Class<?> processed) {
+        Map<Class<? extends Annotation>, LinkedList<AnnotationWrapper<?>>> map = new HashMap<>();
+        if (!currentInterf.equals(processed)) {
+            for (Annotation annotation : currentInterf.getDeclaredAnnotations()) {
+                map.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>())
+                        .add(new AnnotationWrapper<>(annotation, true, currentInterf));
+            }
+        }
+
+        Map<Class<? extends Annotation>, LinkedList<AnnotationWrapper<?>>> parents = new HashMap<>();
+        for (Class<?> parentInterf : currentInterf.getInterfaces()) {
+            Map<Class<? extends Annotation>, LinkedList<AnnotationWrapper<?>>> current = collectInterfaceAnnotations(parentInterf,
+                                                                                                                     processed);
+            current.entrySet().stream()
+                    .filter(entry -> !parents.containsKey(entry.getKey()) || REPEATABLE.contains(entry.getKey()))
+                    .peek(entry -> {
+                        if (parents.containsKey(entry.getKey())) {
+                            throw new JsonbException("Cannot process annotation " + entry.getKey().getName() + " from multiple "
+                                                             + "parallel sources");
+                        }
+                    })
+                    .forEach(entry -> {
+                        parents.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue());
+                        map.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue());
+                    });
+        }
+        return map;
+    }
+
+    //    private void collectParentInterfaceAnnotations(Class<?> currentInterf,
+    //                                                   Map<Class<? extends Annotation>, LinkedList<Annotation>> overall) {
+    //        Map<Class<? extends Annotation>, LinkedList<Annotation>> parents = new HashMap<>();
+    //        for (Class<?> parentInterf : currentInterf.getInterfaces()) {
+    //            collectParentInterfaceAnnotations(parentInterf, );
+    //            current.entrySet().stream()
+    //                    .filter(entry -> parents.containsKey(entry.getKey()) || REPEATABLE.contains(entry.getKey()))
+    //                    .peek(entry -> {
+    //                        if (parents.containsKey(entry.getKey())) {
+    //                            throw new JsonbException("CHANGE THIS EXCEPTION");
+    //                        }
+    //                    })
+    //                    .forEach(entry -> {
+    //                        parents.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue());
+    //                        map.computeIfAbsent(entry.getKey(), aClass -> new LinkedList<>()).addAll(entry.getValue());
+    //                    });
+    //        }
+    //        if (currentInterf.isInterface()) {
+    //            for (Annotation annotation : currentInterf.getDeclaredAnnotations()) {
+    //                map.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>()).add(annotation);
+    //            }
+    //        }
+    //        return map;
+    //    }
+
+    private void addIfNotPresent(JsonbAnnotatedElement<?> element, Class<?> definedType, Annotation... annotations) {
+        for (Annotation annotation : annotations) {
+            if (element.getAnnotation(annotation.annotationType()).isEmpty()
+                    || REPEATABLE.contains(annotation.annotationType())) {
+                element.putAnnotation(annotation, true, definedType);
+            }
+        }
+    }
+
+    public boolean requiredParameters(Executable executable, JsonbAnnotatedElement<Parameter> annotated) {
+        return jsonbContext.getConfigProperties().hasRequiredCreatorParameters();
+        //        if (OPTIONALS.contains(annotated.getElement().getType())) {
+        //            return false;
+        //        }
+        //        return annotated.getAnnotation(JsonbRequired.class)
+        //                .or(() -> Optional.ofNullable(executable.getAnnotation(JsonbRequired.class)))
+        //                .map(JsonbRequired::value)
+        //                .orElseGet(() -> jsonbContext.getConfigProperties().hasRequiredCreatorParameters());
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/BuiltInTypes.java b/src/main/java/org/eclipse/yasson/internal/BuiltInTypes.java
new file mode 100644
index 0000000..6aa12b0
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/BuiltInTypes.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.UUID;
+
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import jakarta.json.JsonValue;
+
+/**
+ * Types which are supported by the Yasson by default.
+ */
+public class BuiltInTypes {
+
+    private static final Set<Class<?>> BUILD_IN_SUPPORT;
+
+    static {
+        Set<Class<?>> buildInTypes = new HashSet<>();
+        buildInTypes.add(Byte.class);
+        buildInTypes.add(Byte.TYPE);
+        buildInTypes.add(BigDecimal.class);
+        buildInTypes.add(BigInteger.class);
+        buildInTypes.add(Boolean.class);
+        buildInTypes.add(Boolean.TYPE);
+        buildInTypes.add(Calendar.class);
+        buildInTypes.add(Character.class);
+        buildInTypes.add(Character.TYPE);
+        buildInTypes.add(Date.class);
+        buildInTypes.add(Double.class);
+        buildInTypes.add(Double.TYPE);
+        buildInTypes.add(Duration.class);
+        buildInTypes.add(Float.class);
+        buildInTypes.add(Float.TYPE);
+        buildInTypes.add(Integer.class);
+        buildInTypes.add(Integer.TYPE);
+        buildInTypes.add(Instant.class);
+        buildInTypes.add(LocalDateTime.class);
+        buildInTypes.add(LocalDate.class);
+        buildInTypes.add(LocalTime.class);
+        buildInTypes.add(Long.class);
+        buildInTypes.add(Long.TYPE);
+        buildInTypes.add(Number.class);
+        buildInTypes.add(OffsetDateTime.class);
+        buildInTypes.add(OffsetTime.class);
+        buildInTypes.add(OptionalDouble.class);
+        buildInTypes.add(OptionalInt.class);
+        buildInTypes.add(OptionalLong.class);
+        buildInTypes.add(Path.class);
+        buildInTypes.add(Period.class);
+        buildInTypes.add(Short.class);
+        buildInTypes.add(Short.TYPE);
+        buildInTypes.add(String.class);
+        buildInTypes.add(TimeZone.class);
+        buildInTypes.add(URI.class);
+        buildInTypes.add(URL.class);
+        buildInTypes.add(UUID.class);
+        if (isClassAvailable("javax.xml.datatype.XMLGregorianCalendar")) {
+            buildInTypes.add(XMLGregorianCalendar.class);
+        }
+        buildInTypes.add(ZonedDateTime.class);
+        buildInTypes.add(ZoneId.class);
+        buildInTypes.add(ZoneOffset.class);
+        if (isClassAvailable("java.sql.Date")) {
+            buildInTypes.add(java.sql.Date.class);
+            buildInTypes.add(java.sql.Timestamp.class);
+        }
+        BUILD_IN_SUPPORT = Set.copyOf(buildInTypes);
+    }
+
+    private BuiltInTypes() {
+        throw new IllegalStateException("Util class cannot be instantiated");
+    }
+
+    /**
+     * Check whether the class is available.
+     *
+     * @param className name of the checked class
+     * @return true if available, otherwise false
+     */
+    public static boolean isClassAvailable(String className) {
+        try {
+            Class.forName(className);
+            return true;
+        } catch (ClassNotFoundException | LinkageError e) {
+            return false;
+        }
+    }
+
+    /**
+     * Whether the type is a supported type by default.
+     *
+     * @param clazz type to check
+     * @return whether is supported
+     */
+    public static boolean isKnownType(Class<?> clazz) {
+        boolean knownContainerValueType = Collection.class.isAssignableFrom(clazz)
+                || Map.class.isAssignableFrom(clazz)
+                || JsonValue.class.isAssignableFrom(clazz)
+                || Optional.class.isAssignableFrom(clazz)
+                || clazz.isArray();
+
+        return knownContainerValueType || findIfClassIsSupported(clazz);
+    }
+
+    private static boolean findIfClassIsSupported(Class<?> clazz) {
+        Class<?> current = clazz;
+        do {
+            if (BUILD_IN_SUPPORT.contains(current)) {
+                return true;
+            }
+            current = current.getSuperclass();
+        } while (current != null);
+        return false;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java b/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
new file mode 100644
index 0000000..72653cf
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Optional;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.model.Property;
+
+/**
+ * Search for instance creator from other sources.
+ * Mainly intended to add extensibility for different java versions and new features.
+ */
+public class ClassMultiReleaseExtension {
+
+    private ClassMultiReleaseExtension() {
+        throw new IllegalStateException("This class cannot be instantiated");
+    }
+
+    static boolean shouldTransformToPropertyName(Method method) {
+        return true;
+    }
+
+    static boolean isSpecialAccessorMethod(Method method, Map<String, Property> classProperties) {
+        return false;
+    }
+
+    static JsonbCreator findCreator(Class<?> clazz,
+                                    Constructor<?>[] declaredConstructors,
+                                    AnnotationIntrospector introspector,
+                                    PropertyNamingStrategy propertyNamingStrategy) {
+        return null;
+    }
+
+    public static boolean isRecord(Class<?> clazz) {
+        return false;
+    }
+
+    public static Optional<JsonbException> exceptionToThrow(Class<?> clazz) {
+        return Optional.empty();
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/ClassParser.java b/src/main/java/org/eclipse/yasson/internal/ClassParser.java
new file mode 100644
index 0000000..4adce65
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/ClassParser.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+
+import org.eclipse.yasson.internal.model.ClassModel;
+import org.eclipse.yasson.internal.model.CreatorModel;
+import org.eclipse.yasson.internal.model.JsonbAnnotatedElement;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.model.Property;
+import org.eclipse.yasson.internal.model.PropertyModel;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Created a class internal model.
+ */
+class ClassParser {
+
+    private static final String IS_PREFIX = "is";
+
+    private static final String GET_PREFIX = "get";
+
+    private static final String SET_PREFIX = "set";
+
+    private final JsonbContext jsonbContext;
+
+    ClassParser(JsonbContext jsonbContext) {
+        this.jsonbContext = jsonbContext;
+    }
+
+    /**
+     * Parse class fields and getters setters. Merge to java bean like properties.
+     */
+    void parseProperties(ClassModel classModel, JsonbAnnotatedElement<Class<?>> classElement) {
+        final Map<String, Property> classProperties = new HashMap<>();
+        parseFields(classElement, classProperties);
+        parseClassAndInterfaceMethods(classElement, classProperties);
+
+        //add sorted properties from parent, if they are not overridden in current class
+        //parent properties are by default first by alphabet, than properties from a subclass
+        final List<PropertyModel> sortedParentProperties = getSortedParentProperties(classModel, classElement, classProperties);
+
+        List<PropertyModel> classPropertyModels = classProperties.values().stream()
+                .map(property -> new PropertyModel(classModel, property, jsonbContext))
+                .collect(Collectors.toList());
+
+        //check for collision on same property read name
+        List<PropertyModel> unsortedMerged = new ArrayList<>(sortedParentProperties.size() + classPropertyModels.size());
+        unsortedMerged.addAll(sortedParentProperties);
+        unsortedMerged.addAll(classPropertyModels);
+        checkPropertyNameClash(unsortedMerged, classModel.getType());
+
+        mergePropertyModels(classPropertyModels);
+
+        List<PropertyModel> sortedPropertyModels = new ArrayList<>(sortedParentProperties.size() + classPropertyModels.size());
+        sortedPropertyModels.addAll(sortedParentProperties);
+        sortedPropertyModels.addAll(jsonbContext.getConfigProperties().getPropertyOrdering()
+                                            .orderProperties(classPropertyModels, classModel));
+
+        //reference property to creator parameter by name to merge configuration in runtime
+        JsonbCreator creator = classModel.getClassCustomization().getCreator();
+        if (creator != null) {
+            sortedPropertyModels.forEach(propertyModel -> {
+                for (CreatorModel creatorModel : creator.getParams()) {
+                    if (creatorModel.getName().equals(propertyModel.getPropertyName())) {
+                        creatorModel.getCustomization().setPropertyModel(propertyModel);
+                    }
+                }
+            });
+        }
+
+        classModel.setProperties(sortedPropertyModels);
+
+    }
+
+    private static void mergePropertyModels(List<PropertyModel> unsortedMerged) {
+        PropertyModel[] clone = unsortedMerged.toArray(new PropertyModel[0]);
+        for (int i = 0; i < clone.length; i++) {
+            for (int j = i + 1; j < clone.length; j++) {
+                PropertyModel firstPropertyModel = clone[i];
+                PropertyModel secondPropertyModel = clone[j];
+                if (firstPropertyModel.equals(secondPropertyModel)) {
+                    // Need to merge two properties
+                    unsortedMerged.remove(firstPropertyModel);
+                    unsortedMerged.remove(secondPropertyModel);
+                    if (!firstPropertyModel.isReadable() && !firstPropertyModel.isWritable()) {
+                        unsortedMerged.add(secondPropertyModel);
+                    } else if (!secondPropertyModel.isReadable() && !secondPropertyModel.isWritable()) {
+                        unsortedMerged.add(firstPropertyModel);
+                    } else {
+                        unsortedMerged.add(new PropertyModel(firstPropertyModel, secondPropertyModel));
+                    }
+                }
+            }
+        }
+    }
+
+    private void parseClassAndInterfaceMethods(JsonbAnnotatedElement<Class<?>> classElement,
+                                               Map<String, Property> classProperties) {
+        Class<?> concreteClass = classElement.getElement();
+        parseMethods(concreteClass, classElement, classProperties);
+        for (Class<?> ifc : jsonbContext.getAnnotationIntrospector().collectInterfaces(concreteClass)) {
+            parseIfaceMethodAnnotations(ifc, classElement, classProperties);
+        }
+    }
+
+    private void parseIfaceMethodAnnotations(Class<?> ifc,
+                                             JsonbAnnotatedElement<Class<?>> classElement,
+                                             Map<String, Property> classProperties) {
+        Method[] declaredMethods = AccessController.doPrivileged((PrivilegedAction<Method[]>) ifc::getDeclaredMethods);
+        for (Method method : declaredMethods) {
+            final String methodName = method.getName();
+            if (!isPropertyMethod(method)) {
+                continue;
+            }
+            String propertyName = toPropertyMethod(methodName);
+
+            Property property = classProperties.get(propertyName);
+
+            if (method.isDefault()) {
+                // Interface provides default implementation
+                if (property == null) {
+                    // the property does not yet exists : create it from scratch
+                    property = registerMethod(propertyName, method, classElement, classProperties);
+                } else {
+                    // property already exists, take care not overriding already parsed implementation
+                    if (isSetter(method)) {
+                        if (property.getSetter() == null) {
+                            property.setSetter(method);
+                        }
+                    } else {
+                        if (property.getGetter() == null) {
+                            property.setGetter(method);
+                        }
+                    }
+                }
+            }
+
+            if (property == null) {
+                //May happen for classes which both extend a class with some method and implement interface with same method.
+                continue;
+            }
+            JsonbAnnotatedElement<Method> methodElement = isGetter(method)
+                    ? property.getGetterElement() : property.getSetterElement();
+            //Only push iface annotations if not overridden on impl classes
+            for (Annotation ann : method.getDeclaredAnnotations()) {
+                if (methodElement.getAnnotation(ann.annotationType()).isEmpty()) {
+                    methodElement.putAnnotation(ann, true, null);
+                }
+            }
+        }
+    }
+
+    private Property registerMethod(String propertyName,
+                                    Method method,
+                                    JsonbAnnotatedElement<Class<?>> classElement,
+                                    Map<String, Property> classProperties) {
+        Property property = classProperties.computeIfAbsent(propertyName, n -> new Property(n, classElement));
+        if (isSetter(method)) {
+            property.setSetter(method);
+        } else {
+            property.setGetter(method);
+        }
+
+        return property;
+    }
+
+    private void parseMethods(Class<?> clazz,
+                              JsonbAnnotatedElement<Class<?>> classElement,
+                              Map<String, Property> classProperties) {
+        Method[] declaredMethods = AccessController.doPrivileged((PrivilegedAction<Method[]>) clazz::getDeclaredMethods);
+        for (Method method : declaredMethods) {
+            String name = method.getName();
+            //isBridge method filters out methods inherited from interfaces
+            boolean isAccessorMethod = ClassMultiReleaseExtension.isSpecialAccessorMethod(method, classProperties)
+                    || isPropertyMethod(method);
+            if (!isAccessorMethod || method.isBridge() || isSpecialCaseMethod(clazz, method)) {
+                continue;
+            }
+            final String propertyName = ClassMultiReleaseExtension.shouldTransformToPropertyName(method)
+                    ? toPropertyMethod(name)
+                    : name;
+
+            registerMethod(propertyName, method, classElement, classProperties);
+        }
+    }
+
+    /**
+     * Filter out certain methods that get forcibly added to some classes.
+     * For example the public groovy.lang.MetaClass X.getMetaClass() method from Groovy classes
+     */
+    private static boolean isSpecialCaseMethod(Class<?> clazz, Method m) {
+        if (!Modifier.isPublic(m.getModifiers()) || Modifier.isStatic(m.getModifiers()) || m.isSynthetic()) {
+            return false;
+        }
+        // Groovy objects will have public groovy.lang.MetaClass X.getMetaClass()
+        // which causes an infinite loop in serialization
+        if (m.getName().equals("getMetaClass")
+                && m.getReturnType().getCanonicalName().equals("groovy.lang.MetaClass")) {
+            return true;
+        }
+        // WELD proxy objects will have 'public org.jboss.weld
+        if (m.getName().equals("getMetadata")
+                && m.getReturnType().getCanonicalName().equals("org.jboss.weld.proxy.WeldClientProxy$Metadata")) {
+            return true;
+        }
+        return false;
+    }
+
+    private static boolean isGetter(Method m) {
+        return (m.getName().startsWith(GET_PREFIX) || m.getName().startsWith(IS_PREFIX)) && m.getParameterCount() == 0;
+    }
+
+    private static boolean isSetter(Method m) {
+        return m.getName().startsWith(SET_PREFIX) && m.getParameterCount() == 1;
+    }
+
+    private static String toPropertyMethod(String name) {
+        return lowerFirstLetter(name.substring(name.startsWith(IS_PREFIX) ? 2 : 3));
+    }
+
+    private static String lowerFirstLetter(String name) {
+        Objects.requireNonNull(name);
+        if (name.length() == 0) {
+            //methods named get() or set()
+            return name;
+        }
+        if (name.length() > 1
+                && Character.isUpperCase(name.charAt(1))
+                && Character.isUpperCase(name.charAt(0))) {
+            return name;
+        }
+        char[] chars = name.toCharArray();
+        chars[0] = Character.toLowerCase(chars[0]);
+        return new String(chars);
+    }
+
+    private static boolean isPropertyMethod(Method m) {
+        return isGetter(m) || isSetter(m);
+    }
+
+    private static void parseFields(JsonbAnnotatedElement<Class<?>> classElement, Map<String, Property> classProperties) {
+        Field[] declaredFields = AccessController.doPrivileged(
+                (PrivilegedAction<Field[]>) () -> classElement.getElement().getDeclaredFields());
+        for (Field field : declaredFields) {
+            final String name = field.getName();
+            if (field.isSynthetic()) {
+                continue;
+            }
+            final Property property = new Property(name, classElement);
+            property.setField(field);
+            classProperties.put(name, property);
+        }
+    }
+
+    private static void checkPropertyNameClash(List<PropertyModel> collectedProperties, Class<?> cls) {
+        final List<PropertyModel> checkedProperties = new ArrayList<>();
+        for (PropertyModel collectedPropertyModel : collectedProperties) {
+            for (PropertyModel checkedPropertyModel : checkedProperties) {
+                if ((checkedPropertyModel.getReadName().equals(collectedPropertyModel.getReadName())
+                        && checkedPropertyModel.isReadable() //
+                        && collectedPropertyModel.isReadable())
+                        || (checkedPropertyModel.getWriteName().equals(collectedPropertyModel.getWriteName())
+                                && checkedPropertyModel.isWritable() //
+                                && collectedPropertyModel.isWritable())) {
+                    throw new JsonbException(
+                            Messages.getMessage(MessageKeys.PROPERTY_NAME_CLASH, checkedPropertyModel.getPropertyName(),
+                                    collectedPropertyModel.getPropertyName(), cls.getName()));
+                }
+            }
+            checkedProperties.add(collectedPropertyModel);
+        }
+    }
+
+    /**
+     * Merges current class properties with parent class properties.
+     * If javabean property is declared in more than one inheritance levels,
+     * merge field, getters and setters of that property.
+     * <p>
+     * For example BaseClass contains field foo and getter getFoo. In BaseExtensions there is a setter setFoo.
+     * All three will be merged for BaseExtension.
+     * <p>
+     * Such property is sorted based on where its getter or field is located.
+     */
+    private List<PropertyModel> getSortedParentProperties(ClassModel classModel,
+                                                          JsonbAnnotatedElement<Class<?>> classElement,
+                                                          Map<String, Property> classProperties) {
+        List<PropertyModel> sortedProperties = new ArrayList<>();
+        //Pull properties from parent
+        if (classModel.getParentClassModel() != null) {
+            for (PropertyModel parentProp : classModel.getParentClassModel().getSortedProperties()) {
+                final Property current = classProperties.get(parentProp.getPropertyName());
+                //don't replace overridden properties
+                if (current == null) {
+                    sortedProperties.add(parentProp);
+                } else {
+                    //merge
+                    final Property merged = mergeProperty(current, parentProp, classElement);
+                    PropertyVisibilityStrategy propertyVisibilityStrategy = classModel.getClassCustomization()
+                            .getPropertyVisibilityStrategy();
+
+                    if (PropertyModel.isPropertyReadable(current.getField(), current.getGetter(), propertyVisibilityStrategy)) {
+                        classProperties.replace(current.getName(), merged);
+                    } else {
+                        sortedProperties.add(new PropertyModel(classModel, merged, jsonbContext));
+                        classProperties.remove(current.getName());
+                    }
+
+                }
+            }
+        }
+        return sortedProperties;
+    }
+
+    /**
+     * Select the correct method to use. The correct method is the most specific
+     * method which is not a default one:
+     * <ul>
+     * <li> if current is not defined, returns parent;</li>
+     * <li> if parent is not defined, returns current;</li>
+     * <li> if current is a default method and parent is not, returns parent;</li>
+     * <ul>
+     * <li><i>By definition, it is not possible to make a choice betweentwo default
+     * methods. <br/>Here, the most specific is selected, but a concrete
+     * implementation MUST eventually be provided as the source code won't even
+     * compile if such a method does not exist</i></li>
+     * </ul>
+     * <li> returns current otherwise</li>
+     * </ul>
+     *
+     * @param current current 'child' implementation
+     * @param parent  parent implementation
+     * @return effective method to register as getter or setter
+     */
+    private static Method selectMostSpecificNonDefaultMethod(Method current, Method parent) {
+        return (
+                current != null ? (
+                        parent != null && current.isDefault()
+                                && !parent.isDefault() ? parent : current) : parent);
+    }
+
+    private static Property mergeProperty(Property current,
+                                          PropertyModel parentProp,
+                                          JsonbAnnotatedElement<Class<?>> classElement) {
+        Field field = current.getField() != null
+                ? current.getField() : parentProp.getField();
+        Method getter = selectMostSpecificNonDefaultMethod(current.getGetter(),
+                                                           parentProp.getGetter());
+        Method setter = selectMostSpecificNonDefaultMethod(current.getSetter(),
+                                                           parentProp.getSetter());
+
+        Property merged = new Property(parentProp.getPropertyName(), classElement);
+        if (field != null) {
+            merged.setField(field);
+        }
+        if (getter != null) {
+            merged.setGetter(getter);
+        }
+        if (setter != null) {
+            merged.setSetter(setter);
+        }
+        return merged;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java
new file mode 100644
index 0000000..b4a2cf8
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.LinkedList;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+
+import org.eclipse.yasson.internal.components.AbstractComponentBinding;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.components.ComponentBindings;
+import org.eclipse.yasson.internal.components.DeserializerBinding;
+import org.eclipse.yasson.internal.components.SerializerBinding;
+import org.eclipse.yasson.internal.model.customization.ComponentBoundCustomization;
+
+/**
+ * Searches for a registered components or Serializer for a given type.
+ */
+public class ComponentMatcher {
+
+    private final JsonbContext jsonbContext;
+
+    /**
+     * Flag for searching for generic serializers and adapters in runtime.
+     */
+    private volatile boolean genericComponents;
+
+    private final ConcurrentMap<Type, ComponentBindings> userComponents;
+
+    /**
+     * Create component matcher.
+     *
+     * @param context mandatory
+     */
+    ComponentMatcher(JsonbContext context) {
+        Objects.requireNonNull(context);
+        this.jsonbContext = context;
+        userComponents = new ConcurrentHashMap<>();
+        init();
+    }
+
+    /**
+     * Called during context creation, introspecting user components provided with JsonbConfig.
+     */
+    void init() {
+        final JsonbSerializer<?>[] serializers = (JsonbSerializer<?>[]) jsonbContext.getConfig()
+                .getProperty(JsonbConfig.SERIALIZERS).orElseGet(() -> new JsonbSerializer<?>[] {});
+        for (JsonbSerializer<?> serializer : serializers) {
+            SerializerBinding<?> serializerBinding = introspectSerializerBinding(serializer.getClass(), serializer);
+            addSerializer(serializerBinding.getBindingType(), serializerBinding);
+        }
+        final JsonbDeserializer<?>[] deserializers = (JsonbDeserializer<?>[]) jsonbContext.getConfig()
+                .getProperty(JsonbConfig.DESERIALIZERS).orElseGet(() -> new JsonbDeserializer<?>[] {});
+        for (JsonbDeserializer<?> deserializer : deserializers) {
+            DeserializerBinding<?> deserializerBinding = introspectDeserializerBinding(deserializer.getClass(), deserializer);
+            addDeserializer(deserializerBinding.getBindingType(), deserializerBinding);
+        }
+
+        final JsonbAdapter<?, ?>[] adapters = (JsonbAdapter<?, ?>[]) jsonbContext.getConfig().getProperty(JsonbConfig.ADAPTERS)
+                .orElseGet(() -> new JsonbAdapter<?, ?>[] {});
+        for (JsonbAdapter<?, ?> adapter : adapters) {
+            AdapterBinding adapterBinding = introspectAdapterBinding(adapter.getClass(), adapter);
+            addAdapter(adapterBinding.getBindingType(), adapterBinding);
+        }
+    }
+
+    private ComponentBindings getBindingInfo(Type type) {
+        return userComponents
+                .compute(type, (type1, bindingInfo) -> bindingInfo != null ? bindingInfo : new ComponentBindings(type1));
+    }
+
+    private void addSerializer(Type bindingType, SerializerBinding<?> serializer) {
+        userComponents.computeIfPresent(bindingType, (type, bindings) -> {
+            if (bindings.getSerializer() != null) {
+                return bindings;
+            }
+            registerGeneric(bindingType);
+            return new ComponentBindings(bindingType, serializer, bindings.getDeserializer(), bindings.getAdapterInfo());
+        });
+    }
+
+    private void addDeserializer(Type bindingType, DeserializerBinding<?> deserializer) {
+        userComponents.computeIfPresent(bindingType, (type, bindings) -> {
+            if (bindings.getDeserializer() != null) {
+                return bindings;
+            }
+            registerGeneric(bindingType);
+            return new ComponentBindings(bindingType, bindings.getSerializer(), deserializer, bindings.getAdapterInfo());
+        });
+    }
+
+    private void addAdapter(Type bindingType, AdapterBinding adapter) {
+        userComponents.computeIfPresent(bindingType, (type, bindings) -> {
+            if (bindings.getAdapterInfo() != null) {
+                return bindings;
+            }
+            registerGeneric(bindingType);
+            return new ComponentBindings(bindingType, bindings.getSerializer(), bindings.getDeserializer(), adapter);
+        });
+    }
+
+    /**
+     * If type is not parametrized runtime component resolution doesn't has to happen.
+     *
+     * @param bindingType component binding type
+     */
+    private void registerGeneric(Type bindingType) {
+        if (bindingType instanceof ParameterizedType && !genericComponents) {
+            genericComponents = true;
+        }
+    }
+
+    /**
+     * Lookup serializer binding for a given property runtime type.
+     *
+     * @param propertyRuntimeType runtime type of a property
+     * @param customization       with component info
+     * @return serializer optional
+     */
+    public Optional<SerializerBinding<?>> getSerializerBinding(Type propertyRuntimeType,
+                                                               ComponentBoundCustomization customization) {
+
+        if (customization == null || customization.getSerializerBinding() == null) {
+            return searchComponentBinding(propertyRuntimeType, ComponentBindings::getSerializer);
+        }
+        return Optional.of(customization.getSerializerBinding());
+    }
+
+    /**
+     * Lookup deserializer binding for a given property runtime type.
+     *
+     * @param propertyRuntimeType runtime type of a property
+     * @param customization       customization with component info
+     * @return serializer optional
+     */
+    public Optional<DeserializerBinding<?>> getDeserializerBinding(Type propertyRuntimeType,
+                                                                   ComponentBoundCustomization customization) {
+        if (customization == null || customization.getDeserializerBinding() == null) {
+            return searchComponentBinding(propertyRuntimeType, ComponentBindings::getDeserializer);
+        }
+        return Optional.of(customization.getDeserializerBinding());
+    }
+
+    /**
+     * Get components from property model (if declared by annotation and runtime type matches),
+     * or return components searched by runtime type.
+     *
+     * @param propertyRuntimeType runtime type not null
+     * @param customization       customization with component info
+     * @return components info if present
+     */
+    public Optional<AdapterBinding> getSerializeAdapterBinding(Type propertyRuntimeType,
+                                                               ComponentBoundCustomization customization) {
+        if (customization == null || customization.getSerializeAdapterBinding() == null) {
+            return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterInfo);
+        }
+        return Optional.of(customization.getSerializeAdapterBinding());
+    }
+
+    /**
+     * Get components from property model (if declared by annotation and runtime type matches),
+     * or return components searched by runtime type.
+     *
+     * @param propertyRuntimeType runtime type not null
+     * @param customization       customization with component info
+     * @return components info if present
+     */
+    public Optional<AdapterBinding> getDeserializeAdapterBinding(Type propertyRuntimeType,
+                                                                 ComponentBoundCustomization customization) {
+        if (customization == null || customization.getDeserializeAdapterBinding() == null) {
+            return searchComponentBinding(propertyRuntimeType, ComponentBindings::getAdapterInfo);
+        }
+        return Optional.of(customization.getDeserializeAdapterBinding());
+    }
+
+    private <T extends AbstractComponentBinding> Optional<T> searchComponentBinding(Type runtimeType, Function<ComponentBindings, T> supplier) {
+        // First check if there is an exact match
+        ComponentBindings binding = userComponents.get(runtimeType);
+        if (binding != null) {
+            Optional<T> match = getMatchingBinding(runtimeType, binding, supplier);
+            if (match.isPresent()) {
+               return match;
+            }
+        }
+        
+        Optional<Class<?>> runtimeClass = ReflectionUtils.getOptionalRawType(runtimeType);
+        if (runtimeClass.isPresent()) {
+            // Check if any interfaces have a match
+            for (Class<?> ifc : runtimeClass.get().getInterfaces()) {
+                ComponentBindings ifcBinding = userComponents.get(ifc);
+                if (ifcBinding != null) {
+                  Optional<T> match = getMatchingBinding(ifc, ifcBinding, supplier);
+                  if (match.isPresent()) {
+                      return match;
+                  }
+                }
+            }
+            
+            // check if the superclass has a match
+            Class<?> superClass = runtimeClass.get().getSuperclass();
+            if (superClass != null && superClass != Object.class) {
+                Optional<T> superBinding = searchComponentBinding(superClass, supplier);
+                if (superBinding.isPresent()) {
+                    return superBinding;
+                }
+            }
+        }
+        
+        return Optional.empty();
+    }
+    
+    private <T> Optional<T> getMatchingBinding(Type runtimeType, ComponentBindings binding, Function<ComponentBindings, T> supplier) {
+        final T component = supplier.apply(binding);
+        if (component != null && matches(runtimeType, binding.getBindingType())) {
+            return Optional.of(component);
+        }
+        return Optional.empty();
+    }
+
+    private boolean matches(Type runtimeType, Type componentBindingType) {
+        if (componentBindingType.equals(runtimeType)) {
+            return true;
+        }
+
+        if (componentBindingType instanceof Class && runtimeType instanceof Class) {
+            return ((Class<?>) componentBindingType).isAssignableFrom((Class<?>) runtimeType);
+        }
+
+        //don't try to runtime generic scan if not needed
+        if (!genericComponents) {
+            return false;
+        }
+
+        return runtimeType instanceof ParameterizedType && componentBindingType instanceof ParameterizedType
+                && ReflectionUtils.getRawType(componentBindingType).isAssignableFrom(ReflectionUtils.getRawType(runtimeType))
+                && matchTypeArguments((ParameterizedType) runtimeType, (ParameterizedType) componentBindingType);
+    }
+
+    /**
+     * If runtimeType to adapt is a ParametrizedType, check all type args to match against components args.
+     */
+    private boolean matchTypeArguments(ParameterizedType requiredType, ParameterizedType componentBound) {
+        final Type[] requiredTypeArguments = requiredType.getActualTypeArguments();
+        final Type[] adapterBoundTypeArguments = componentBound.getActualTypeArguments();
+        if (requiredTypeArguments.length != adapterBoundTypeArguments.length) {
+            return false;
+        }
+        for (int i = 0; i < requiredTypeArguments.length; i++) {
+            Type adapterTypeArgument = adapterBoundTypeArguments[i];
+            if (!requiredTypeArguments[i].equals(adapterTypeArgument)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Introspect components generic information and put resolved types into metadata wrapper.
+     *
+     * @param adapterClass class of an components
+     * @param instance     components instance
+     * @return introspected info with resolved typevar types.
+     */
+    AdapterBinding introspectAdapterBinding(Class<? extends JsonbAdapter> adapterClass, JsonbAdapter instance) {
+        final ParameterizedType adapterRuntimeType = ReflectionUtils.findParameterizedType(adapterClass, JsonbAdapter.class);
+        final Type[] adapterTypeArguments = adapterRuntimeType.getActualTypeArguments();
+        Type adaptFromType = resolveTypeArg(adapterTypeArguments[0], adapterClass);
+        Type adaptToType = resolveTypeArg(adapterTypeArguments[1], adapterClass);
+        final ComponentBindings componentBindings = getBindingInfo(adaptFromType);
+        if (componentBindings.getAdapterInfo() != null && componentBindings.getAdapterInfo().getAdapter().getClass()
+                .equals(adapterClass)) {
+            return componentBindings.getAdapterInfo();
+        }
+        JsonbAdapter newAdapter = instance != null
+                ? instance
+                : jsonbContext.getComponentInstanceCreator().getOrCreateComponent(adapterClass);
+        return new AdapterBinding(adaptFromType, adaptToType, newAdapter);
+    }
+
+    /**
+     * If an instance of deserializerClass is present in context and is bound for same type, return that instance.
+     * Otherwise create new instance and set it to context.
+     *
+     * @param deserializerClass class of deserializer
+     * @param instance          instance to use if not cached already
+     * @return wrapper used in property models
+     */
+    @SuppressWarnings("unchecked")
+    DeserializerBinding introspectDeserializerBinding(Class<? extends JsonbDeserializer> deserializerClass,
+                                                      JsonbDeserializer instance) {
+        final ParameterizedType deserializerRuntimeType = ReflectionUtils
+                .findParameterizedType(deserializerClass, JsonbDeserializer.class);
+        Type deserializerBindingType = resolveTypeArg(deserializerRuntimeType.getActualTypeArguments()[0], deserializerClass);
+        final ComponentBindings componentBindings = getBindingInfo(deserializerBindingType);
+        if (componentBindings.getDeserializer() != null && componentBindings.getDeserializer().getClass()
+                .equals(deserializerClass)) {
+            return componentBindings.getDeserializer();
+        } else {
+            JsonbDeserializer deserializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator()
+                    .getOrCreateComponent(deserializerClass);
+            return new DeserializerBinding(deserializerBindingType, deserializer);
+        }
+    }
+
+    /**
+     * If an instance of serializerClass is present in context and is bound for same type, return that instance.
+     * Otherwise create new instance and set it to context.
+     *
+     * @param serializerClass class of deserializer
+     * @param instance        instance to use if not cached
+     * @return wrapper used in property models
+     */
+    @SuppressWarnings("unchecked")
+    SerializerBinding introspectSerializerBinding(Class<? extends JsonbSerializer> serializerClass, JsonbSerializer instance) {
+        final ParameterizedType serializerRuntimeType = ReflectionUtils
+                .findParameterizedType(serializerClass, JsonbSerializer.class);
+        Type serBindingType = resolveTypeArg(serializerRuntimeType.getActualTypeArguments()[0], serializerClass);
+        final ComponentBindings componentBindings = getBindingInfo(serBindingType);
+        if (componentBindings.getSerializer() != null && componentBindings.getSerializer().getClass().equals(serializerClass)) {
+            return componentBindings.getSerializer();
+        } else {
+            JsonbSerializer serializer = instance != null ? instance : jsonbContext.getComponentInstanceCreator()
+                    .getOrCreateComponent(serializerClass);
+            return new SerializerBinding(serBindingType, serializer);
+        }
+
+    }
+
+    private Type resolveTypeArg(Type adapterTypeArg, Type adapterType) {
+        if (adapterTypeArg instanceof ParameterizedType) {
+            return ReflectionUtils.resolveTypeArguments((ParameterizedType) adapterTypeArg, adapterType);
+        } else if (adapterTypeArg instanceof TypeVariable) {
+            LinkedList<Type> chain = new LinkedList<>();
+            chain.add(adapterType);
+            return ReflectionUtils.resolveItemVariableType(chain, (TypeVariable<?>) adapterTypeArg, true);
+        } else {
+            return adapterTypeArg;
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospector.java b/src/main/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospector.java
new file mode 100644
index 0000000..7d3fe87
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospector.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Parameter;
+import java.util.Arrays;
+import java.util.logging.Logger;
+
+import org.eclipse.yasson.internal.model.CreatorModel;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+class ConstructorPropertiesAnnotationIntrospector {
+
+    private static final Logger LOG = Logger.getLogger(ConstructorPropertiesAnnotationIntrospector.class.getName());
+
+    private final JsonbContext jsonbContext;
+    private final AnnotationFinder constructorProperties;
+
+    public static ConstructorPropertiesAnnotationIntrospector forContext(JsonbContext jsonbContext) {
+        return new ConstructorPropertiesAnnotationIntrospector(jsonbContext, AnnotationFinder.findConstructorProperties());
+    }
+
+    /**
+     * Only for testing and internal purposes.
+     * <p>
+     * Please use static factory methods e.g. {@link #forContext(JsonbContext)}.
+     *
+     * @param context          {@link JsonbContext}
+     * @param annotationFinder {@link AnnotationFinder}
+     */
+    protected ConstructorPropertiesAnnotationIntrospector(JsonbContext context, AnnotationFinder annotationFinder) {
+        this.jsonbContext = context;
+        this.constructorProperties = annotationFinder;
+    }
+
+    public JsonbCreator getCreator(Constructor<?>[] constructors) {
+        JsonbCreator jsonbCreator = null;
+
+        for (Constructor<?> constructor : constructors) {
+            Object properties = constructorProperties.valueIn(constructor.getDeclaredAnnotations());
+            if (!(properties instanceof String[])) {
+                continue;
+            }
+            if (!Modifier.isPublic(constructor.getModifiers())) {
+                String declaringClass = constructor.getDeclaringClass().getName();
+                String message = "The constructor of {0} annotated with @ConstructorProperties {1} is not accessible and will "
+                        + "be ignored.";
+                LOG.finest(String.format(message, declaringClass, Arrays.toString((String[]) properties)));
+                continue;
+            }
+            if (jsonbCreator != null) {
+                // don't fail in this case, because it is perfectly allowed to have more than one
+                // @ConstructorProperties-Annotation in general.
+                // It is just undefined, which constructor to choose for JSON in this case.
+                // The behavior should be the same (null), as if there is no ConstructorProperties-Annotation at all.
+                LOG.warning(Messages.getMessage(MessageKeys.MULTIPLE_CONSTRUCTOR_PROPERTIES_CREATORS,
+                                                constructor.getDeclaringClass().getName()));
+                return null;
+            }
+            jsonbCreator = createJsonbCreator(constructor, (String[]) properties);
+        }
+        return jsonbCreator;
+    }
+
+    private JsonbCreator createJsonbCreator(Executable executable, String[] properties) {
+        final Parameter[] parameters = executable.getParameters();
+
+        CreatorModel[] creatorModels = new CreatorModel[parameters.length];
+        for (int i = 0; i < parameters.length; i++) {
+            final Parameter parameter = parameters[i];
+            creatorModels[i] = new CreatorModel(properties[i], parameter, executable, jsonbContext);
+        }
+        return new JsonbCreator(executable, creatorModels);
+    }
+
+    @Override
+    public String toString() {
+        return "ConstructorPropertiesAnnotationIntrospector [jsonbContext=" + jsonbContext + ", constructorProperties=" + constructorProperties + "]";
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/DeserializationContextImpl.java b/src/main/java/org/eclipse/yasson/internal/DeserializationContextImpl.java
new file mode 100644
index 0000000..e568a79
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/DeserializationContextImpl.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+import org.eclipse.yasson.internal.model.customization.Customization;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserialization context implementation.
+ */
+public class DeserializationContextImpl extends ProcessingContext implements DeserializationContext {
+    private final List<Runnable> delayedSetters = new ArrayList<>();
+    private JsonParser.Event lastValueEvent;
+    private Customization customization = ClassCustomization.empty();
+    private Object instance;
+
+    /**
+     * Parent instance for marshaller and unmarshaller.
+     *
+     * @param jsonbContext context of Jsonb
+     */
+    public DeserializationContextImpl(JsonbContext jsonbContext) {
+        super(jsonbContext);
+    }
+
+    /**
+     * Create new instance based on previous context.
+     *
+     * @param context previous deserialization context
+     */
+    public DeserializationContextImpl(DeserializationContextImpl context) {
+        super(context.getJsonbContext());
+        this.lastValueEvent = context.lastValueEvent;
+    }
+
+    /**
+     * Return instance of currently deserialized type.
+     *
+     * @return null if instance has not been created yet
+     */
+    public Object getInstance() {
+        return instance;
+    }
+
+    /**
+     * Set currently deserialized type instance.
+     *
+     * @param instance deserialized type instance
+     */
+    public void setInstance(Object instance) {
+        this.instance = instance;
+    }
+
+    /**
+     * Return the list of deferred deserializers.
+     *
+     * @return list of deferred deserializers
+     */
+    public List<Runnable> getDeferredDeserializers() {
+        return delayedSetters;
+    }
+
+    /**
+     * Return last obtained {@link JsonParser.Event} event.
+     *
+     * @return last obtained event
+     */
+    public JsonParser.Event getLastValueEvent() {
+        return lastValueEvent;
+    }
+
+    /**
+     * Set last obtained {@link JsonParser.Event} event.
+     *
+     * @param lastValueEvent last obtained event
+     */
+    public void setLastValueEvent(JsonParser.Event lastValueEvent) {
+        this.lastValueEvent = lastValueEvent;
+    }
+
+    /**
+     * Return customization used by currently processed user defined deserializer.
+     *
+     * @return currently used customization
+     */
+    public Customization getCustomization() {
+        return customization;
+    }
+
+    /**
+     * Set customization used by currently processed user defined deserializer.
+     *
+     * @param customization currently used customization
+     */
+    public void setCustomization(Customization customization) {
+        this.customization = customization;
+    }
+
+    @Override
+    public <T> T deserialize(Class<T> clazz, JsonParser parser) {
+        return deserializeItem(clazz, parser);
+    }
+
+    @Override
+    public <T> T deserialize(Type type, JsonParser parser) {
+        return deserializeItem(type, parser);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> T deserializeItem(Type type, JsonParser parser) {
+        try {
+            if (lastValueEvent == null) {
+                lastValueEvent = parser.next();
+                checkState();
+            }
+            ModelDeserializer<JsonParser> modelDeserializer = getJsonbContext().getChainModelCreator().deserializerChain(type);
+            return (T) modelDeserializer.deserialize(parser, this);
+        } catch (JsonbException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, e.getMessage()), e);
+        }
+    }
+
+    private void checkState() {
+        if (lastValueEvent == JsonParser.Event.KEY_NAME) {
+            throw new JsonbException("JsonParser has incorrect position as the first event: KEY_NAME");
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/InstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/InstanceCreator.java
new file mode 100644
index 0000000..11173bd
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/InstanceCreator.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.function.Supplier;
+
+/**
+ * Creates instances for known types, caches constructors of unknown.
+ * (Constructors of parsed types are stored in {@link org.eclipse.yasson.internal.model.ClassModel}).
+ */
+public class InstanceCreator {
+
+    private static final Map<Class<?>, Supplier<?>> CREATORS = new HashMap<>();
+
+    static {
+        CREATORS.put(ArrayList.class, ArrayList::new);
+        CREATORS.put(LinkedList.class, LinkedList::new);
+        CREATORS.put(HashSet.class, HashSet::new);
+        CREATORS.put(TreeSet.class, TreeSet::new);
+        CREATORS.put(HashMap.class, HashMap::new);
+        CREATORS.put(TreeMap.class, TreeMap::new);
+    }
+
+    private InstanceCreator() {
+        throw new IllegalStateException("This class should never be instantiated");
+    }
+
+    /**
+     * Create an instance of the given class with its default constructor.
+     *
+     * @param tClass class to create instance
+     * @param <T>    Type of the class/instance
+     * @return crated instance
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T createInstance(Class<T> tClass) {
+        Supplier<T> creator = (Supplier<T>) CREATORS.get(tClass);
+        //No worries for race conditions here, instance may be replaced during first attempt.
+        if (creator == null) {
+            Constructor<T> constructor = ReflectionUtils.getDefaultConstructor(tClass, true);
+            creator = () -> ReflectionUtils.createNoArgConstructorInstance(constructor);
+            CREATORS.put(tClass, creator);
+        }
+
+        return creator.get();
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/JsonBinding.java b/src/main/java/org/eclipse/yasson/internal/JsonBinding.java
new file mode 100644
index 0000000..fb185de
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/JsonBinding.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2016, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.json.JsonStructure;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.spi.JsonProvider;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.YassonJsonb;
+import org.eclipse.yasson.internal.jsonstructure.JsonGeneratorToStructureAdapter;
+import org.eclipse.yasson.internal.jsonstructure.JsonStructureToParserAdapter;
+
+/**
+ * Implementation of Jsonb interface.
+ */
+public class JsonBinding implements YassonJsonb {
+
+    private final JsonbContext jsonbContext;
+
+    JsonBinding(JsonBindingBuilder builder) {
+        this.jsonbContext = new JsonbContext(builder.getConfig(), builder.getProvider().orElseGet(JsonProvider::provider));
+        Set<Class<?>> eagerInitClasses = this.jsonbContext.getConfigProperties().getEagerInitClasses();
+        for (Class<?> eagerInitClass : eagerInitClasses) {
+            // Eagerly initialize requested ClassModels and Serializers
+            jsonbContext.getChainModelCreator().deserializerChain(eagerInitClass);
+            jsonbContext.getSerializationModelCreator().serializerChain(eagerInitClass, true, true);
+        }
+    }
+
+    private <T> T deserialize(final Type type, final JsonParser parser, final DeserializationContextImpl unmarshaller) {
+        return unmarshaller.deserialize(type, parser);
+    }
+
+    @Override
+    public <T> T fromJson(String str, Class<T> type) throws JsonbException {
+        try (JsonParser parser = jsonbContext.getJsonProvider().createParser(new StringReader(str))) {
+            final DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
+            return deserialize(type, parser, unmarshaller);
+        }
+    }
+
+    @Override
+    public <T> T fromJson(String str, Type type) throws JsonbException {
+        try (JsonParser parser = jsonbContext.getJsonProvider().createParser(new StringReader(str))) {
+            DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
+            return deserialize(type, parser, unmarshaller);
+        }
+    }
+
+    @Override
+    public <T> T fromJson(Reader reader, Class<T> type) throws JsonbException {
+        try (JsonParser parser = jsonbContext.getJsonProvider().createParser(reader)) {
+            DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
+            return deserialize(type, parser, unmarshaller);
+        }
+    }
+
+    @Override
+    public <T> T fromJson(Reader reader, Type type) throws JsonbException {
+        try (JsonParser parser = jsonbContext.getJsonProvider().createParser(reader)) {
+            DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
+            return deserialize(type, parser, unmarshaller);
+        }
+    }
+
+    @Override
+    public <T> T fromJson(InputStream stream, Class<T> clazz) throws JsonbException {
+        DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
+        try (JsonParser parser = inputStreamParser(stream)) {
+            return deserialize(clazz, parser, unmarshaller);
+        }
+    }
+
+    @Override
+    public <T> T fromJson(InputStream stream, Type type) throws JsonbException {
+        DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
+        try (JsonParser parser = inputStreamParser(stream)) {
+            return deserialize(type, parser, unmarshaller);
+        }
+    }
+
+    @Override
+    public <T> T fromJsonStructure(JsonStructure jsonStructure, Class<T> type) throws JsonbException {
+        try (JsonParser parser = new JsonStructureToParserAdapter(jsonStructure)) {
+            return deserialize(type, parser, new DeserializationContextImpl(jsonbContext));
+        }
+    }
+
+    @Override
+    public <T> T fromJsonStructure(JsonStructure jsonStructure, Type runtimeType) throws JsonbException {
+        try (JsonParser parser = new JsonStructureToParserAdapter(jsonStructure)) {
+            return deserialize(runtimeType, parser, new DeserializationContextImpl(jsonbContext));
+        }
+    }
+
+    private JsonParser inputStreamParser(InputStream stream) {
+        return jsonbContext.getJsonParserFactory()
+                .createParser(stream,
+                              Charset.forName((String) jsonbContext.getConfig()
+                                      .getProperty(JsonbConfig.ENCODING).orElse("UTF-8")));
+    }
+
+    @Override
+    public String toJson(Object object) throws JsonbException {
+        StringWriter writer = new StringWriter();
+        try (JsonGenerator generator = writerGenerator(writer)) {
+            new SerializationContextImpl(jsonbContext).marshall(object, generator);
+        }
+        return writer.toString();
+    }
+
+    @Override
+    public String toJson(Object object, Type type) throws JsonbException {
+        StringWriter writer = new StringWriter();
+        try (JsonGenerator generator = writerGenerator(writer)) {
+            new SerializationContextImpl(jsonbContext, type).marshall(object, generator);
+        }
+        return writer.toString();
+    }
+
+    @Override
+    public void toJson(Object object, Writer writer) throws JsonbException {
+        final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext);
+        try (JsonGenerator generator = writerGenerator(writer)) {
+            marshaller.marshallWithoutClose(object, generator);
+        }
+    }
+
+    @Override
+    public void toJson(Object object, Type type, Writer writer) throws JsonbException {
+        final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, type);
+        try (JsonGenerator generator = writerGenerator(writer)) {
+            marshaller.marshallWithoutClose(object, generator);
+        }
+    }
+
+    private JsonGenerator writerGenerator(Writer writer) {
+        Map<String, ?> factoryProperties = jsonbContext.createJsonpProperties(jsonbContext.getConfig());
+        if (factoryProperties.isEmpty()) {
+            return jsonbContext.getJsonProvider().createGenerator(writer);
+        }
+        return jsonbContext.getJsonProvider().createGeneratorFactory(factoryProperties).createGenerator(writer);
+    }
+
+    @Override
+    public void toJson(Object object, OutputStream stream) throws JsonbException {
+        final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext);
+        try (JsonGenerator generator = streamGenerator(stream)) {
+            marshaller.marshall(object, generator);
+        }
+    }
+
+    @Override
+    public void toJson(Object object, Type type, OutputStream stream) throws JsonbException {
+        final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, type);
+        try (JsonGenerator generator = streamGenerator(stream)) {
+            marshaller.marshall(object, generator);
+        }
+    }
+
+    @Override
+    public <T> T fromJson(JsonParser jsonParser, Class<T> type) throws JsonbException {
+        DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
+        return unmarshaller.deserialize(type, jsonParser);
+    }
+
+    @Override
+    public <T> T fromJson(JsonParser jsonParser, Type runtimeType) throws JsonbException {
+        DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
+        return unmarshaller.deserialize(runtimeType, jsonParser);
+    }
+
+    @Override
+    public void toJson(Object object, JsonGenerator jsonGenerator) throws JsonbException {
+        final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext);
+        marshaller.marshallWithoutClose(object, jsonGenerator);
+    }
+
+    @Override
+    public void toJson(Object object, Type runtimeType, JsonGenerator jsonGenerator) throws JsonbException {
+        final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, runtimeType);
+        marshaller.marshallWithoutClose(object, jsonGenerator);
+    }
+
+    @Override
+    public JsonStructure toJsonStructure(Object object) throws JsonbException {
+        JsonGeneratorToStructureAdapter structureGenerator = new JsonGeneratorToStructureAdapter(jsonbContext.getJsonProvider());
+        final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext);
+        marshaller.marshall(object, structureGenerator);
+        return structureGenerator.getRootStructure();
+    }
+
+    @Override
+    public JsonStructure toJsonStructure(Object object, Type runtimeType) throws JsonbException {
+        JsonGeneratorToStructureAdapter structureGenerator = new JsonGeneratorToStructureAdapter(jsonbContext.getJsonProvider());
+        final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, runtimeType);
+        marshaller.marshall(object, structureGenerator);
+        return structureGenerator.getRootStructure();
+    }
+
+    private JsonGenerator streamGenerator(OutputStream stream) {
+        Map<String, ?> factoryProperties = jsonbContext.createJsonpProperties(jsonbContext.getConfig());
+        final String encoding = (String) jsonbContext.getConfig().getProperty(JsonbConfig.ENCODING).orElse("UTF-8");
+        return jsonbContext.getJsonProvider().createGeneratorFactory(factoryProperties)
+                .createGenerator(stream, Charset.forName(encoding));
+    }
+
+    @Override
+    public void close() throws Exception {
+        jsonbContext.getComponentInstanceCreator().close();
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/JsonBindingBuilder.java b/src/main/java/org/eclipse/yasson/internal/JsonBindingBuilder.java
new file mode 100644
index 0000000..33725e9
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/JsonBindingBuilder.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.util.Optional;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.spi.JsonProvider;
+
+/**
+ * JsonbBuilder implementation.
+ */
+public class JsonBindingBuilder implements JsonbBuilder {
+    private JsonbConfig config = new JsonbConfig();
+    private JsonProvider provider = null;
+
+    @Override
+    public JsonbBuilder withConfig(JsonbConfig config) {
+        this.config = config;
+        return this;
+    }
+
+    @Override
+    public JsonbBuilder withProvider(JsonProvider jsonpProvider) {
+        this.provider = jsonpProvider;
+        return this;
+    }
+
+    /**
+     * Gets configuration.
+     *
+     * @return configuration.
+     */
+    public JsonbConfig getConfig() {
+        return config;
+    }
+
+    /**
+     * Gets provider.
+     *
+     * @return Provider.
+     */
+    public Optional<JsonProvider> getProvider() {
+        return Optional.ofNullable(provider);
+    }
+
+    @Override
+    public Jsonb build() {
+        return new JsonBinding(this);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/JsonbConfigProperties.java b/src/main/java/org/eclipse/yasson/internal/JsonbConfigProperties.java
new file mode 100644
index 0000000..668ac20
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/JsonbConfigProperties.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 2017, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020 Payara Foundation 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.temporal.ChronoField;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.bind.config.BinaryDataStrategy;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+import jakarta.json.bind.config.PropertyOrderStrategy;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+import jakarta.json.bind.serializer.JsonbSerializer;
+
+import org.eclipse.yasson.YassonConfig;
+import org.eclipse.yasson.internal.model.PropertyModel;
+import org.eclipse.yasson.internal.model.ReverseTreeMap;
+import org.eclipse.yasson.internal.model.customization.PropertyOrdering;
+import org.eclipse.yasson.internal.model.customization.StrategiesProvider;
+import org.eclipse.yasson.internal.model.customization.VisibilityStrategiesProvider;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Resolved properties from JSONB config.
+ */
+@SuppressWarnings("rawtypes")
+public class JsonbConfigProperties {
+
+    private static final Map<String, Class<? extends Map>> PROPERTY_ORDER_STRATEGY_MAPS =
+            Map.of(PropertyOrderStrategy.LEXICOGRAPHICAL, TreeMap.class,
+                   PropertyOrderStrategy.REVERSE, ReverseTreeMap.class,
+                   PropertyOrderStrategy.ANY, HashMap.class);
+
+    private final JsonbConfig jsonbConfig;
+    private final PropertyVisibilityStrategy propertyVisibilityStrategy;
+    private final PropertyNamingStrategy propertyNamingStrategy;
+    private final PropertyOrdering propertyOrdering;
+    private final JsonbDateFormatter dateFormatter;
+    private final Locale locale;
+    private final String binaryDataStrategy;
+    private final boolean nullable;
+    private final boolean failOnUnknownProperties;
+    private final boolean strictIJson;
+    private final boolean zeroTimeDefaulting;
+    private final boolean requiredCreatorParameters;
+    private final boolean dateInMillisecondsAsString;
+    private final Map<Class<?>, Class<?>> userTypeMapping;
+    private final Class<?> defaultMapImplType;
+    private final JsonbSerializer<Object> nullSerializer;
+    private final Set<Class<?>> eagerInitClasses;
+    private final boolean forceMapArraySerializerForNullKeys;
+
+    /**
+     * Creates new resolved JSONB config.
+     *
+     * @param jsonbConfig jsonb config
+     */
+    public JsonbConfigProperties(JsonbConfig jsonbConfig) {
+        this.jsonbConfig = jsonbConfig;
+        this.binaryDataStrategy = initBinaryDataStrategy();
+        this.propertyNamingStrategy = initPropertyNamingStrategy();
+        this.propertyVisibilityStrategy = initPropertyVisibilityStrategy();
+        this.propertyOrdering = new PropertyOrdering(initOrderStrategy());
+        this.locale = initConfigLocale();
+        this.dateFormatter = initDateFormatter(this.locale);
+        this.nullable = initConfigNullable();
+        this.failOnUnknownProperties = initConfigFailOnUnknownProperties();
+        this.strictIJson = initStrictJson();
+        this.userTypeMapping = initUserTypeMapping();
+        this.zeroTimeDefaulting = initZeroTimeDefaultingForJavaTime();
+        this.defaultMapImplType = initDefaultMapImplType();
+        this.nullSerializer = initNullSerializer();
+        this.eagerInitClasses = initEagerInitClasses();
+        this.requiredCreatorParameters = initRequiredCreatorParameters();
+        this.forceMapArraySerializerForNullKeys = initForceMapArraySerializerForNullKeys();
+        this.dateInMillisecondsAsString = initDateInMillisecondsAsString();
+    }
+
+    private Class<? extends Map> initDefaultMapImplType() {
+        //We need to get PropertyOrderStrategy again. This time, if was not set, use ANY to get proper map implementation.
+        //This is intentional!
+        String propertyOrder = getConfigProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY, String.class, PropertyOrderStrategy.ANY);
+        return PROPERTY_ORDER_STRATEGY_MAPS.getOrDefault(propertyOrder, HashMap.class);
+    }
+
+    private boolean initZeroTimeDefaultingForJavaTime() {
+        return getConfigProperty(YassonConfig.ZERO_TIME_PARSE_DEFAULTING, Boolean.class, false);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<Class<?>, Class<?>> initUserTypeMapping() {
+        return getConfigProperty(YassonConfig.USER_TYPE_MAPPING, Map.class, Collections.emptyMap());
+    }
+
+    private JsonbDateFormatter initDateFormatter(Locale locale) {
+        final String dateFormat = getGlobalConfigJsonbDateFormat();
+        if (JsonbDateFormat.DEFAULT_FORMAT.equals(dateFormat) || JsonbDateFormat.TIME_IN_MILLIS.equals(dateFormat)) {
+            return new JsonbDateFormatter(dateFormat, locale.toLanguageTag());
+        }
+        DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().appendPattern(dateFormat);
+        if (isZeroTimeDefaulting()) {
+            builder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
+            builder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0);
+            builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
+        }
+        return new JsonbDateFormatter(builder.toFormatter(locale), dateFormat, locale.toLanguageTag());
+    }
+
+    private String getGlobalConfigJsonbDateFormat() {
+        return getConfigProperty(JsonbConfig.DATE_FORMAT, String.class, JsonbDateFormat.DEFAULT_FORMAT);
+    }
+
+    private Consumer<List<PropertyModel>> initOrderStrategy() {
+        return StrategiesProvider.getOrderingFunction(getPropertyOrderStrategy());
+    }
+
+    private String getPropertyOrderStrategy() {
+        return getConfigProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY, String.class, PropertyOrderStrategy.LEXICOGRAPHICAL);
+    }
+
+    private PropertyNamingStrategy initPropertyNamingStrategy() {
+        final Optional<Object> property = jsonbConfig.getProperty(JsonbConfig.PROPERTY_NAMING_STRATEGY);
+        if (property.isEmpty()) {
+            return StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.IDENTITY);
+        }
+        Object propertyNamingStrategy = property.get();
+        if (propertyNamingStrategy instanceof String) {
+            return StrategiesProvider.getPropertyNamingStrategy((String) propertyNamingStrategy);
+        } else if (!(propertyNamingStrategy instanceof PropertyNamingStrategy)) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.PROPERTY_NAMING_STRATEGY_INVALID));
+        }
+        return (PropertyNamingStrategy) property.get();
+    }
+
+    private PropertyVisibilityStrategy initPropertyVisibilityStrategy() {
+        final Optional<Object> property = jsonbConfig.getProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY);
+        if (property.isEmpty()) {
+            return null;
+        }
+        final Object propertyVisibilityStrategy = property.get();
+        if (propertyVisibilityStrategy instanceof String) {
+            return VisibilityStrategiesProvider.getStrategy((String) propertyVisibilityStrategy);
+        } else if (!(propertyVisibilityStrategy instanceof PropertyVisibilityStrategy)) {
+            throw new JsonbException("JsonbConfig.PROPERTY_VISIBILITY_STRATEGY must be instance of " + PropertyVisibilityStrategy.class);
+        }
+        return (PropertyVisibilityStrategy) propertyVisibilityStrategy;
+    }
+
+    private String initBinaryDataStrategy() {
+        if (getConfigProperty(JsonbConfig.STRICT_IJSON, Boolean.class, false)) {
+            return BinaryDataStrategy.BASE_64_URL;
+        }
+        return getConfigProperty(JsonbConfig.BINARY_DATA_STRATEGY, String.class, BinaryDataStrategy.BYTE);
+    }
+
+    private boolean initConfigNullable() {
+        return getConfigProperty(JsonbConfig.NULL_VALUES, Boolean.class, false);
+    }
+
+    private boolean initConfigFailOnUnknownProperties() {
+        return getConfigProperty(YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES, Boolean.class, false);
+    }
+
+    private boolean initRequiredCreatorParameters() {
+        String sysProp = AccessController.doPrivileged((PrivilegedAction<String>)
+                () -> System.getProperty(JsonbConfig.CREATOR_PARAMETERS_REQUIRED));
+
+        if (sysProp != null) {
+            return Boolean.parseBoolean(sysProp);
+        }
+        return getConfigProperty(JsonbConfig.CREATOR_PARAMETERS_REQUIRED, Boolean.class, false);
+    }
+
+    private boolean initDateInMillisecondsAsString() {
+        String sysProp = AccessController.doPrivileged((PrivilegedAction<String>)
+                () -> System.getProperty(YassonConfig.DATE_TIME_IN_MILLIS_AS_A_STRING));
+
+        if (sysProp != null) {
+            return Boolean.parseBoolean(sysProp);
+        }
+        return getConfigProperty(YassonConfig.DATE_TIME_IN_MILLIS_AS_A_STRING, Boolean.class, false);
+    }
+
+    @SuppressWarnings("unchecked")
+    private JsonbSerializer<Object> initNullSerializer() {
+        return jsonbConfig.getProperty(YassonConfig.NULL_ROOT_SERIALIZER)
+                .map(o -> {
+                    if (!(o instanceof JsonbSerializer)) {
+                        throw new JsonbException("YassonConfig.NULL_ROOT_SERIALIZER must be instance of " + JsonbSerializer.class
+                                                         + "<Object>");
+                    }
+                    return (JsonbSerializer<Object>) o;
+                }).orElse(null);
+    }
+
+    private Set<Class<?>> initEagerInitClasses() {
+        Optional<Object> property = jsonbConfig.getProperty(YassonConfig.EAGER_PARSE_CLASSES);
+        if (property.isEmpty()) {
+            return Collections.emptySet();
+        }
+        Object eagerInitClasses = property.get();
+        if (!(eagerInitClasses instanceof Class<?>[])) {
+            throw new JsonbException("YassonConfig.EAGER_PARSE_CLASSES must be instance of Class<?>[]");
+        }
+        return new HashSet<>(Arrays.asList((Class<?>[]) eagerInitClasses));
+    }
+
+    private boolean initForceMapArraySerializerForNullKeys() {
+        return getConfigProperty(YassonConfig.FORCE_MAP_ARRAY_SERIALIZER_FOR_NULL_KEYS, Boolean.class, false);
+    }
+
+    /**
+     * Gets nullable from {@link JsonbConfig}.
+     * If true null values are serialized to json.
+     *
+     * @return Configured nullable
+     */
+    public boolean getConfigNullable() {
+        return nullable;
+    }
+
+    /**
+     * Gets unknown properties flag from {@link JsonbConfig}.
+     * If false, {@link JsonbException} is not thrown for deserialization, when json key
+     * cannot be mapped to class property.
+     *
+     * @return {@link JsonbException} is risen on unknown property. Default is true even if
+     * not set in json config.
+     */
+    public boolean getConfigFailOnUnknownProperties() {
+        return failOnUnknownProperties;
+    }
+
+    private <T> T getConfigProperty(String propertyName, Class<T> propertyType, T defaultValue) {
+        Objects.requireNonNull(defaultValue, "Default value cannot be null");
+        return jsonbConfig.getProperty(propertyName)
+                .or(() -> Optional.of(defaultValue))
+                .filter(propertyType::isInstance)
+                .map(propertyType::cast)
+                .orElseThrow(() -> new JsonbException(Messages.getMessage(MessageKeys.JSONB_CONFIG_PROPERTY_INVALID_TYPE,
+                                                                          propertyName,
+                                                                          propertyType.getSimpleName())));
+    }
+
+    /**
+     * Checks for binary data strategy to use.
+     *
+     * @return Binary data strategy.
+     */
+    public String getBinaryDataStrategy() {
+        return binaryDataStrategy;
+    }
+
+    /**
+     * Converts string locale to {@link Locale}.
+     *
+     * @param locale Locale to convert.
+     * @return {@link Locale} instance.
+     */
+    public Locale getLocale(String locale) {
+        if (locale.equals(JsonbDateFormat.DEFAULT_LOCALE)) {
+            return this.locale;
+        }
+        return Locale.forLanguageTag(locale);
+    }
+
+    /**
+     * Gets locale from {@link JsonbConfig}.
+     *
+     * @return Configured locale.
+     */
+    private Locale initConfigLocale() {
+        return getConfigProperty(JsonbConfig.LOCALE, Locale.class, Locale.getDefault());
+    }
+
+    private boolean initStrictJson() {
+        return getConfigProperty(JsonbConfig.STRICT_IJSON, Boolean.class, false);
+    }
+
+    /**
+     * Gets property visibility strategy.
+     *
+     * @return Property visibility strategy.
+     */
+    public PropertyVisibilityStrategy getPropertyVisibilityStrategy() {
+        return propertyVisibilityStrategy;
+    }
+
+    /**
+     * Gets property naming strategy.
+     *
+     * @return Property naming strategy.
+     */
+    public PropertyNamingStrategy getPropertyNamingStrategy() {
+        return propertyNamingStrategy;
+    }
+
+    /**
+     * Gets instantiated shared config date formatter.
+     *
+     * @return Date formatter.
+     */
+    public JsonbDateFormatter getConfigDateFormatter() {
+        return dateFormatter;
+    }
+
+    /**
+     * Gets property ordering component.
+     *
+     * @return Component for ordering properties.
+     */
+    public PropertyOrdering getPropertyOrdering() {
+        return propertyOrdering;
+    }
+
+    /**
+     * If strict IJSON patterns should be used.
+     *
+     * @return if IJSON is enabled
+     */
+    public boolean isStrictIJson() {
+        return strictIJson;
+    }
+
+    /**
+     * User type mapping for map interface to implementation classes.
+     *
+     * @return User type mapping.
+     */
+    public Map<Class<?>, Class<?>> getUserTypeMapping() {
+        return userTypeMapping;
+    }
+
+    /**
+     * <p>Makes parsing dates defaulting to zero hour, minute and second.
+     * This will made available to parse patterns like yyyy.MM.dd to
+     * {@link java.util.Date}, {@link java.util.Calendar}, {@link java.time.Instant} {@link java.time.LocalDate}
+     * or even {@link java.time.ZonedDateTime}.
+     * <p>If time zone is not set in the pattern than UTC time zone is used.
+     * So for example json value 2018.01.01 becomes 2018.01.01 00:00:00 UTC when parsed
+     * to instant {@link java.time.Instant}.
+     *
+     * @return true if time should be defaulted to zero.
+     */
+    public boolean isZeroTimeDefaulting() {
+        return zeroTimeDefaulting;
+    }
+
+    /**
+     * Default {@link java.util.Map} implementation to use, based on order strategy.
+     *
+     * @return map impl type
+     */
+    public Class<?> getDefaultMapImplType() {
+        return defaultMapImplType;
+    }
+
+    public JsonbSerializer<Object> getNullSerializer() {
+        return nullSerializer;
+    }
+
+    public boolean hasRequiredCreatorParameters() {
+        return requiredCreatorParameters;
+    }
+
+    public Set<Class<?>> getEagerInitClasses() {
+        return eagerInitClasses;
+    }
+
+    /**
+     * Whether the MapToEntriesArraySerializer is selected when a null key
+     * is detected in a map.
+     *
+     * @return false or true
+     */
+    public boolean isForceMapArraySerializerForNullKeys() {
+        return forceMapArraySerializerForNullKeys;
+    }
+
+    public boolean isDateInMillisecondsAsString() {
+        return dateInMillisecondsAsString;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/JsonbContext.java b/src/main/java/org/eclipse/yasson/internal/JsonbContext.java
new file mode 100644
index 0000000..721e67e
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/JsonbContext.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.logging.Logger;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.spi.JsonProvider;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParserFactory;
+
+import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory;
+import org.eclipse.yasson.internal.deserializer.DeserializationModelCreator;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+import org.eclipse.yasson.internal.serializer.SerializationModelCreator;
+import org.eclipse.yasson.spi.JsonbComponentInstanceCreator;
+
+/**
+ * Jsonb context holding central components and configuration of jsonb runtime. Scoped to instance of Jsonb runtime.
+ */
+public class JsonbContext {
+
+    private static final Logger LOGGER = Logger.getLogger(JsonbContext.class.getName());
+
+    private final JsonbConfig jsonbConfig;
+
+    private final MappingContext mappingContext;
+
+    private final DeserializationModelCreator deserializationModelCreator;
+
+    private final SerializationModelCreator serializationModelCreator;
+
+    private final JsonbComponentInstanceCreator componentInstanceCreator;
+
+    private final JsonProvider jsonProvider;
+
+    private final JsonParserFactory jsonParserFactory;
+
+    private final ComponentMatcher componentMatcher;
+
+    private final AnnotationIntrospector annotationIntrospector;
+
+    private final JsonbConfigProperties configProperties;
+
+    /**
+     * Creates and initialize context.
+     *
+     * @param jsonbConfig  jsonb jsonbConfig not null
+     * @param jsonProvider provider of JSONP
+     */
+    public JsonbContext(JsonbConfig jsonbConfig, JsonProvider jsonProvider) {
+        Objects.requireNonNull(jsonbConfig);
+        this.jsonbConfig = jsonbConfig;
+        this.mappingContext = new MappingContext(this);
+        this.componentInstanceCreator = initComponentInstanceCreator();
+        this.componentMatcher = new ComponentMatcher(this);
+        this.annotationIntrospector = new AnnotationIntrospector(this);
+        this.jsonProvider = jsonProvider;
+        this.jsonParserFactory = initJsonParserFactory();
+        this.configProperties = new JsonbConfigProperties(jsonbConfig);
+        this.deserializationModelCreator = new DeserializationModelCreator(this);
+        this.serializationModelCreator = new SerializationModelCreator(this);
+    }
+
+    /**
+     * Gets {@link JsonbConfig}.
+     *
+     * @return Configuration.
+     */
+    public JsonbConfig getConfig() {
+        return jsonbConfig;
+    }
+
+    /**
+     * Gets mapping context.
+     *
+     * @return Mapping context.
+     */
+    public MappingContext getMappingContext() {
+        return mappingContext;
+    }
+
+    /**
+     * Get chain model creator.
+     *
+     * @return chain model creator
+     */
+    public DeserializationModelCreator getChainModelCreator() {
+        return deserializationModelCreator;
+    }
+
+    /**
+     * Get serialization model creator.
+     *
+     * @return serialization model creator
+     */
+    public SerializationModelCreator getSerializationModelCreator() {
+        return serializationModelCreator;
+    }
+
+    /**
+     * Gets JSONP provider.
+     *
+     * @return JSONP provider.
+     */
+    public JsonProvider getJsonProvider() {
+        return jsonProvider;
+    }
+
+    /**
+     * Implementation creating instances of user components used by JSONB, such as adapters and strategies.
+     *
+     * @return Instance creator.
+     */
+    public JsonbComponentInstanceCreator getComponentInstanceCreator() {
+        return componentInstanceCreator;
+    }
+
+    /**
+     * Component matcher for lookup of (de)serializers and adapters.
+     *
+     * @return Component matcher.
+     */
+    public ComponentMatcher getComponentMatcher() {
+        return componentMatcher;
+    }
+
+    /**
+     * Gets component for annotation parsing.
+     *
+     * @return Annotation introspector.
+     */
+    public AnnotationIntrospector getAnnotationIntrospector() {
+        return annotationIntrospector;
+    }
+
+    public JsonbConfigProperties getConfigProperties() {
+        return configProperties;
+    }
+
+    public JsonParserFactory getJsonParserFactory() {
+        return jsonParserFactory;
+    }
+
+    private JsonParserFactory initJsonParserFactory() {
+        return jsonProvider.createParserFactory(createJsonpProperties(jsonbConfig));
+    }
+
+    /**
+     * Propagates properties from JsonbConfig to JSONP generator / parser factories.
+     *
+     * @param jsonbConfig jsonb config
+     * @return properties for JSONP generator / parser
+     */
+    protected Map<String, ?> createJsonpProperties(JsonbConfig jsonbConfig) {
+        //JSONP 1.0 actually ignores the value, just checks the key is present. Only set if JsonbConfig.FORMATTING is true.
+        final Optional<Object> property = jsonbConfig.getProperty(JsonbConfig.FORMATTING);
+        final Map<String, Object> factoryProperties = new HashMap<>();
+        if (property.isPresent()) {
+            final Object value = property.get();
+            if (!(value instanceof Boolean)) {
+                throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_CONFIG_FORMATTING_ILLEGAL_VALUE));
+            }
+            if ((Boolean) value) {
+                factoryProperties.put(JsonGenerator.PRETTY_PRINTING, Boolean.TRUE);
+            }
+            return factoryProperties;
+        }
+        return factoryProperties;
+    }
+
+    private JsonbComponentInstanceCreator initComponentInstanceCreator() {
+        ServiceLoader<JsonbComponentInstanceCreator> loader = AccessController
+                .doPrivileged((PrivilegedAction<ServiceLoader<JsonbComponentInstanceCreator>>) () -> ServiceLoader
+                        .load(JsonbComponentInstanceCreator.class));
+        List<JsonbComponentInstanceCreator> creators = new ArrayList<>();
+        for (JsonbComponentInstanceCreator creator : loader) {
+            creators.add(creator);
+        }
+        if (creators.isEmpty()) {
+            // No service provider found - use the defaults
+            return JsonbComponentInstanceCreatorFactory.getComponentInstanceCreator();
+        }
+        creators.sort(Comparator.comparingInt(JsonbComponentInstanceCreator::getPriority).reversed());
+        JsonbComponentInstanceCreator creator = creators.get(0);
+        LOGGER.finest("Component instance creator:" + creator.getClass());
+        return creator;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/JsonbDateFormatter.java b/src/main/java/org/eclipse/yasson/internal/JsonbDateFormatter.java
new file mode 100644
index 0000000..9887640
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/JsonbDateFormatter.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.Locale;
+import java.util.Objects;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+
+import static java.time.temporal.ChronoField.HOUR_OF_DAY;
+import static java.time.temporal.ChronoField.MINUTE_OF_HOUR;
+import static java.time.temporal.ChronoField.SECOND_OF_MINUTE;
+
+/**
+ * Formatter wrapper for different types of dates.
+ */
+public class JsonbDateFormatter {
+
+    private static final JsonbDateFormatter DEFAULT = new JsonbDateFormatter(JsonbDateFormat.DEFAULT_FORMAT,
+                                                                             Locale.getDefault().toLanguageTag());
+
+    /**
+     * Default I-JSON date time formatter.
+     */
+    public static final DateTimeFormatter IJSON_DATE_FORMATTER = new DateTimeFormatterBuilder()
+            .parseCaseInsensitive()
+            .append(DateTimeFormatter.ISO_LOCAL_DATE)
+            .appendLiteral('T')
+            .appendValue(HOUR_OF_DAY, 2)
+            .appendLiteral(':')
+            .appendValue(MINUTE_OF_HOUR, 2)
+            .appendLiteral(':')
+            .appendValue(SECOND_OF_MINUTE, 2)
+            .appendLiteral('Z')
+            .appendOffset("+HH:MM", "+00:00")
+            .toFormatter();
+
+    private final DateTimeFormatter dateTimeFormatter;
+    private final String format;
+    private final String locale;
+
+    /**
+     * Creates an instance with cached {@link DateTimeFormatter}, format and locale.
+     *
+     * @param dateTimeFormatter Reused time formatter.
+     * @param format            Format in string.
+     * @param locale            Locale in string.
+     */
+    public JsonbDateFormatter(DateTimeFormatter dateTimeFormatter, String format, String locale) {
+        this.dateTimeFormatter = dateTimeFormatter;
+        this.format = format;
+        this.locale = locale;
+    }
+
+    /**
+     * Creates an instance with format string and locale.
+     * Formatter will be created on every formatting / parsing operation.
+     *
+     * @param format Formatter format.
+     * @param locale Locale in string.
+     */
+    public JsonbDateFormatter(String format, String locale) {
+        this.format = format;
+        this.locale = locale;
+        this.dateTimeFormatter = null;
+    }
+
+    /**
+     * Creates an instance with cached instance of {@link DateTimeFormatter}.
+     *
+     * @return Formatter instance.
+     */
+    public DateTimeFormatter getDateTimeFormatter() {
+        return dateTimeFormatter;
+    }
+
+    /**
+     * Format string to be used either by formatter.
+     * Needed for formatting {@link java.util.Date} with {@link java.text.SimpleDateFormat},
+     * which is not threadsafe.
+     *
+     * @return Format.
+     */
+    public String getFormat() {
+        return format;
+    }
+
+    /**
+     * Locale to use with formatter.
+     *
+     * @return Locale.
+     */
+    public String getLocale() {
+        return locale;
+    }
+
+    public static JsonbDateFormatter getDefault() {
+        return DEFAULT;
+    }
+
+    public boolean isDefault() {
+        return JsonbDateFormat.DEFAULT_FORMAT.equals(format);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        JsonbDateFormatter that = (JsonbDateFormatter) o;
+        return Objects.equals(format, that.format)
+                && Objects.equals(locale, that.locale)
+                && Objects.equals(dateTimeFormatter, that.dateTimeFormatter);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(dateTimeFormatter, format, locale);
+    }
+
+    @Override
+    public String toString() {
+        return "JsonbDateFormatter{"
+                + "dateTimeFormatter=" + dateTimeFormatter
+                + ", format='" + format + '\''
+                + ", locale='" + locale + '\''
+                + '}';
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/JsonbNumberFormatter.java b/src/main/java/org/eclipse/yasson/internal/JsonbNumberFormatter.java
new file mode 100644
index 0000000..ee97d86
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/JsonbNumberFormatter.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.util.Objects;
+
+/**
+ * Formatter for numbers.
+ */
+public class JsonbNumberFormatter {
+
+    private final String format;
+
+    private final String locale;
+
+    /**
+     * Construct with format string and locale.
+     *
+     * @param format formatter format
+     * @param locale locale
+     */
+    public JsonbNumberFormatter(String format, String locale) {
+        this.format = format;
+        this.locale = locale;
+    }
+
+    /**
+     * Format string to be used either by formatter.
+     *
+     * @return format
+     */
+    public String getFormat() {
+        return format;
+    }
+
+    /**
+     * Locale to use with formatter.
+     *
+     * @return locale
+     */
+    public String getLocale() {
+        return locale;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        JsonbNumberFormatter that = (JsonbNumberFormatter) o;
+        return Objects.equals(format, that.format)
+                && Objects.equals(locale, that.locale);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(format, locale);
+    }
+
+    @Override
+    public String toString() {
+        return "JsonbNumberFormatter{"
+                + "format='" + format + '\''
+                + ", locale='" + locale + '\''
+                + '}';
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/MappingContext.java b/src/main/java/org/eclipse/yasson/internal/MappingContext.java
new file mode 100644
index 0000000..9a91c25
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/MappingContext.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2015, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+
+import org.eclipse.yasson.internal.model.ClassModel;
+import org.eclipse.yasson.internal.model.JsonbAnnotatedElement;
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+
+/**
+ * JSONB mappingContext. Created once per {@link jakarta.json.bind.Jsonb} instance. Represents a global scope.
+ * Holds internal model.
+ *
+ * Thread safe.
+ */
+public class MappingContext {
+    private final JsonbContext jsonbContext;
+
+    private final ConcurrentHashMap<Class<?>, ClassModel> classes = new ConcurrentHashMap<>();
+
+    private final ClassParser classParser;
+
+    /**
+     * Create mapping context which is scoped to jsonb runtime.
+     *
+     * @param jsonbContext Context. Required.
+     */
+    public MappingContext(JsonbContext jsonbContext) {
+        Objects.requireNonNull(jsonbContext);
+        this.jsonbContext = jsonbContext;
+        this.classParser = new ClassParser(jsonbContext);
+    }
+
+    /**
+     * Searches for class model for given class. Returns the existing instance. Creates a new instance if
+     * it doesn't exist.
+     *
+     * @param clazz Class to search by or parse, not null.
+     * @return {@link ClassModel} for given class.
+     */
+    public ClassModel getOrCreateClassModel(Class<?> clazz) {
+        ClassModel classModel = classes.get(clazz);
+        if (classModel != null) {
+            return classModel;
+        }
+
+        Deque<Class<?>> newClassModels = new ArrayDeque<>();
+        for (Class<?> classToParse = clazz; classToParse != Object.class; classToParse = classToParse.getSuperclass()) {
+            if (classToParse == null) {
+                break;
+            }
+            newClassModels.push(classToParse);
+        }
+        if (clazz == Object.class) {
+            return classes.computeIfAbsent(clazz, (c) -> new ClassModel(c, ClassCustomization.empty(), null, null));
+        }
+
+        ClassModel parentClassModel = null;
+        while (!newClassModels.isEmpty()) {
+            Class<?> toParse = newClassModels.pop();
+            parentClassModel = classes
+                    .computeIfAbsent(toParse, createParseClassModelFunction(parentClassModel, classParser, jsonbContext));
+        }
+        return classes.get(clazz);
+    }
+
+    private static Function<Class<?>, ClassModel> createParseClassModelFunction(ClassModel parentClassModel,
+                                                                                ClassParser classParser,
+                                                                                JsonbContext jsonbContext) {
+        return aClass -> {
+            JsonbAnnotatedElement<Class<?>> clsElement = jsonbContext.getAnnotationIntrospector().collectAnnotations(aClass);
+            ClassCustomization customization = jsonbContext.getAnnotationIntrospector()
+                    .introspectCustomization(clsElement,
+                                             parentClassModel == null
+                                                     ? ClassCustomization.empty()
+                                                     : parentClassModel.getClassCustomization(),
+                                             jsonbContext.getConfigProperties().getPropertyNamingStrategy());
+            //            PolymorphismSupport configPolymorphism = jsonbContext.getConfigProperties().getPolymorphismSupport();
+//            if (configPolymorphism != null) {
+//                customization = mergeConfigAndAnnotationPolymorphism(configPolymorphism,
+//                                                                     configPolymorphism.getClassPolymorphism(aClass),
+//                                                                     customization,
+//                                                                     aClass);
+//            }
+            ClassModel newClassModel = new ClassModel(aClass,
+                                                      customization,
+                                                      parentClassModel,
+                                                      jsonbContext.getConfigProperties().getPropertyNamingStrategy());
+            if (!BuiltInTypes.isKnownType(aClass)) {
+                classParser.parseProperties(newClassModel, clsElement);
+            }
+            return newClassModel;
+        };
+    }
+
+//    private static ClassCustomization mergeConfigAndAnnotationPolymorphism(PolymorphismSupport generalPolymorphism,
+//                                                                           Optional<Polymorphism> maybeClassPolymorphism,
+//                                                                           ClassCustomization customization,
+//                                                                           Class<?> aClass) {
+//        PolymorphismConfig polymorphismConfig = customization.getPolymorphismConfig();
+//        PolymorphismConfig.Builder polyConfigBuilder;
+//        if (polymorphismConfig != null) {
+//            polyConfigBuilder = PolymorphismConfig.builder().of(polymorphismConfig);
+//        } else {
+//            polyConfigBuilder = PolymorphismConfig.builder();
+//            maybeClassPolymorphism.ifPresent(classPolymorphism -> polyConfigBuilder
+//                    .inherited(!classPolymorphism.getBoundClass().equals(aClass)));
+//        }
+//        generalPolymorphism.getKeyName().filter(s -> !s.isEmpty()).ifPresent(polyConfigBuilder::fieldName);
+//        generalPolymorphism.useClassNames().ifPresent(polyConfigBuilder::useClassNames);
+//        polyConfigBuilder.whitelistedPackages(generalPolymorphism.getWhitelistedPackages());
+//
+//        maybeClassPolymorphism.ifPresent(classPolymorphism -> {
+//            classPolymorphism.getKeyName().filter(s -> !s.isEmpty()).ifPresent(polyConfigBuilder::fieldName);
+//            classPolymorphism.useClassNames().ifPresent(polyConfigBuilder::useClassNames);
+//            classPolymorphism.getFormat().ifPresent(polyConfigBuilder::format);
+//            classPolymorphism.getAliases().forEach(polyConfigBuilder::alias);
+//            polyConfigBuilder.whitelistedPackages(classPolymorphism.getWhitelistedPackages());
+//        });
+//        PolymorphismConfig polyConfigMerged = polyConfigBuilder.build();
+//        if (polyConfigMerged.getFieldName() == null || polyConfigMerged.getFieldName().isEmpty()) {
+//            throw new JsonbException("Polymorphism type field name cannot be null or empty: " + aClass);
+//        }
+//        return ClassCustomization.builder()
+//                .of(customization)
+//                .polymorphismConfig(polyConfigMerged)
+//                .build();
+//    }
+
+    /**
+     * Search for class model, without parsing if not found.
+     *
+     * @param clazz Class to search by or parse, not null.
+     * @return Model of a class if found.
+     */
+    public ClassModel getClassModel(Class<?> clazz) {
+        return classes.get(clazz);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/ProcessingContext.java b/src/main/java/org/eclipse/yasson/internal/ProcessingContext.java
new file mode 100644
index 0000000..17d2b6c
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/ProcessingContext.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Jsonb processing (serializing/deserializing) context.
+ * Instance is thread bound (in contrast to {@link JsonbContext}.
+ */
+public abstract class ProcessingContext {
+
+    private final JsonbContext jsonbContext;
+
+    /**
+     * Used to avoid StackOverflowError, when adapted / serialized object
+     * contains instance of its type inside it or when object has recursive reference.
+     */
+    private final Set<Object> currentlyProcessedObjects = new HashSet<>();
+
+    /**
+     * Parent for marshaller and unmarshaller.
+     *
+     * @param jsonbContext context of Jsonb
+     */
+    public ProcessingContext(JsonbContext jsonbContext) {
+        this.jsonbContext = jsonbContext;
+    }
+
+    /**
+     * Jsonb context.
+     *
+     * @return jsonb context
+     */
+    public JsonbContext getJsonbContext() {
+        return jsonbContext;
+    }
+
+    /**
+     * Mapping context.
+     *
+     * @return mapping context
+     */
+    public MappingContext getMappingContext() {
+        return getJsonbContext().getMappingContext();
+    }
+
+    /**
+     * Adds currently processed object to the {@link Set}.
+     *
+     * @param object processed object
+     * @return if object was added
+     */
+    public boolean addProcessedObject(Object object) {
+        return this.currentlyProcessedObjects.add(object);
+    }
+
+    /**
+     * Removes processed object from the {@link Set}.
+     *
+     * @param object processed object
+     * @return if object was removed
+     */
+    public boolean removeProcessedObject(Object object) {
+        return currentlyProcessedObjects.remove(object);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java b/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java
new file mode 100644
index 0000000..66ccac9
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2015, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Utility class for resolution of generics during unmarshalling.
+ */
+public class ReflectionUtils {
+
+    private static final Logger LOGGER = Logger.getLogger(ReflectionUtils.class.getName());
+
+    private ReflectionUtils() {
+        throw new IllegalStateException("Utility classes should not be instantiated.");
+    }
+
+    /**
+     * Get raw type by type.
+     * Only for ParametrizedTypes, GenericArrayTypes and Classes.
+     *
+     * Empty optional is returned if raw type cannot be resolved.
+     *
+     * @param type Type to get class information from, not null.
+     * @return Class of a type.
+     */
+    public static Optional<Class<?>> getOptionalRawType(Type type) {
+        if (type instanceof Class) {
+            return Optional.of((Class<?>) type);
+        } else if (type instanceof ParameterizedType) {
+            return Optional.of((Class<?>) ((ParameterizedType) type).getRawType());
+        } else if (type instanceof GenericArrayType) {
+            return Optional.of(((GenericArrayType) type).getClass());
+        } else if (type instanceof TypeVariable) {
+            TypeVariable<?> typeVariable = TypeVariable.class.cast(type);
+            if (Objects.nonNull(typeVariable.getBounds())) {
+                Optional<Class<?>> specializedClass = Optional.empty();
+                for (Type bound : typeVariable.getBounds()) {
+                    Optional<Class<?>> boundRawType = getOptionalRawType(bound);
+                    if (boundRawType.isPresent() && !Object.class.equals(boundRawType.get())) {
+                        if (!specializedClass.isPresent() || specializedClass.get().isAssignableFrom(boundRawType.get())) {
+                            specializedClass = Optional.of(boundRawType.get());
+                        }
+                    }
+                }
+                return specializedClass;
+            }
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Get raw type by type.
+     * Resolves only ParametrizedTypes, GenericArrayTypes and Classes.
+     *
+     * Exception is thrown if raw type cannot be resolved.
+     *
+     * @param type Type to get class information from, not null.
+     * @return Class of a raw type.
+     */
+    public static Class<?> getRawType(Type type) {
+        return getOptionalRawType(type)
+                .orElseThrow(() -> new JsonbException(Messages.getMessage(MessageKeys.TYPE_RESOLUTION_ERROR, type)));
+    }
+
+    /**
+     * Get a raw type of any type.
+     * If type is a {@link TypeVariable} recursively search type chain for resolution of typevar.
+     * If type is a {@link WildcardType} find most specific upper / lower bound, which can be used. If most specific
+     * bound is a {@link TypeVariable}, perform typevar resolution.
+     *
+     * @param chain hierarchy of all wrapping types.
+     * @param type  type to resolve, typically field type or generic bound, not null.
+     * @return resolved raw class
+     */
+    public static Class<?> resolveRawType(List<Type> chain, Type type) {
+        if (type instanceof Class) {
+            return (Class<?>) type;
+        } else if (type instanceof ParameterizedType) {
+            return (Class<?>) ((ParameterizedType) type).getRawType();
+        } else {
+            return getRawType(resolveType(chain, type));
+        }
+    }
+
+    /**
+     * Resolve a type by chain.
+     * If type is a {@link TypeVariable} recursively search type chain for resolution of typevar.
+     * If type is a {@link WildcardType} find most specific upper / lower bound, which can be used. If most specific
+     * bound is a {@link TypeVariable}, perform typevar resolution.
+     *
+     * @param chain hierarchy of all wrapping types.
+     * @param type  type to resolve, typically field type or generic bound, not null.
+     * @return resolved type
+     */
+    public static Type resolveType(List<Type> chain, Type type) {
+        return resolveType(chain, type, true);
+    }
+
+    private static Type resolveType(List<Type> chain, Type type, boolean warn) {
+        Type toResolve = type;
+        if (type instanceof GenericArrayType) {
+            toResolve = ((GenericArrayType) type).getGenericComponentType();
+            Type resolved = resolveType(chain, toResolve);
+            return new GenericArrayTypeImpl(resolved);
+        }
+        if (toResolve instanceof WildcardType) {
+            return resolveMostSpecificBound(chain, (WildcardType) toResolve, warn);
+        } else if (toResolve instanceof TypeVariable) {
+            return resolveItemVariableType(chain, (TypeVariable<?>) toResolve, warn);
+        } else if (toResolve instanceof ParameterizedType) {
+            return resolveTypeArguments((ParameterizedType) toResolve, chain.get(chain.size() - 1));
+        }
+        return type;
+    }
+
+    /**
+     * Resolves type by item information and wraps it with {@link Optional}.
+     *
+     * @param chain hierarchy of all wrapping types.
+     * @param type  type
+     * @return resolved type wrapped with Optional
+     */
+    public static Optional<Type> resolveOptionalType(List<Type> chain, Type type) {
+        try {
+            return Optional.of(resolveType(chain, type, false));
+        } catch (RuntimeException e) {
+            return Optional.empty();
+        }
+    }
+
+    /**
+     * Resolve a bounded type variable type by its wrapper types.
+     * Resolution could be done only if a compile time generic information is provided, either:
+     * by generic field or subclass of a generic class.
+     *
+     * @param chain        chain to search "runtime" generic type of a TypeVariable.
+     * @param typeVariable type to search in chain for, not null.
+     * @param warn         whether or not to log a warning message when bounds are not found
+     * @return Type of a generic "runtime" bound, not null.
+     */
+    public static Type resolveItemVariableType(List<Type> chain, TypeVariable<?> typeVariable, boolean warn) {
+//        if (chain == null) {
+//        Optional<Class<?>> optionalRawType = getOptionalRawType(typeVariable);
+//        if (optionalRawType.isPresent()) {
+//            return optionalRawType.get();
+//        }
+
+        //            //Bound not found, treat it as an Object.class
+//            if (warn) {
+//                LOGGER.warning(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND,
+//                                                   typeVariable,
+//                                                   typeVariable.getGenericDeclaration()));
+//            }
+//            return Object.class;
+//        }
+        Type returnType = typeVariable;
+        for (int i = chain.size() - 1; i >= 0; i--) {
+            Type type = chain.get(i);
+            Type tmp = new VariableTypeInheritanceSearch().searchParametrizedType(type, (TypeVariable<?>) returnType);
+            if (tmp != null) {
+                returnType = tmp;
+            }
+            if (!(returnType instanceof TypeVariable)) {
+                break;
+            }
+        }
+        if (returnType instanceof TypeVariable) {
+            //            throw new JsonbException("Could not resolve: " + unresolvedType);
+            return Object.class;
+        }
+        return returnType;
+
+//        //Embedded items doesn't hold information about variable types
+//        if (chain instanceof EmbeddedItem) {
+//            return resolveItemVariableType(chain.getWrapper(), typeVariable, warn);
+//        }
+//
+//        ParameterizedType wrapperParameterizedType = findParameterizedSuperclass(chain.getRuntimeType());
+//
+//        VariableTypeInheritanceSearch search = new VariableTypeInheritanceSearch();
+//        Type foundType = search.searchParametrizedType(wrapperParameterizedType, typeVariable);
+//        if (foundType != null) {
+//            if (foundType instanceof TypeVariable) {
+//                return resolveItemVariableType(chain.getWrapper(), (TypeVariable<?>) foundType, warn);
+//            }
+//            return foundType;
+//        }
+//
+//        return resolveItemVariableType(chain.getWrapper(), typeVariable, warn);
+    }
+
+    /**
+     * Resolves {@link TypeVariable} arguments of generic types.
+     *
+     * @param typeToResolve type to resolve
+     * @param typeToSearch  type to search
+     * @return resolved type
+     */
+    public static Type resolveTypeArguments(ParameterizedType typeToResolve, Type typeToSearch) {
+        final Type[] unresolvedArgs = typeToResolve.getActualTypeArguments();
+        Type[] resolvedArgs = new Type[unresolvedArgs.length];
+        for (int i = 0; i < unresolvedArgs.length; i++) {
+            Type unresolvedArg = unresolvedArgs[i];
+            if (!(unresolvedArg instanceof TypeVariable) && !(unresolvedArg instanceof GenericArrayType)) {
+                resolvedArgs[i] = unresolvedArg;
+            } else {
+                Type variableType = unresolvedArg;
+                if (variableType instanceof GenericArrayType) {
+                    variableType = ((GenericArrayType) variableType).getGenericComponentType();
+                }
+                resolvedArgs[i] = new VariableTypeInheritanceSearch()
+                        .searchParametrizedType(typeToSearch, (TypeVariable<?>) variableType);
+                
+                if (resolvedArgs[i] == null) {
+                    Type[] bounds = ((TypeVariable<?>) variableType).getBounds();
+                    if (Objects.nonNull(bounds) && bounds.length > 0) {
+                        resolvedArgs[i] = bounds[0];
+                    }
+                }
+                
+                if (resolvedArgs[i] == null) {
+                    if (typeToSearch instanceof Class) {
+                        return Object.class;
+                    }
+                    //No generic information available
+                    throw new IllegalStateException(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND,
+                                                                        variableType,
+                                                                        typeToSearch));
+                }
+            }
+            if (resolvedArgs[i] instanceof ParameterizedType) {
+                resolvedArgs[i] = resolveTypeArguments((ParameterizedType) resolvedArgs[i], typeToSearch);
+            } else if (unresolvedArg instanceof GenericArrayType) {
+                resolvedArgs[i] = new GenericArrayTypeImpl(resolvedArgs[i]);
+            }
+        }
+        return Arrays.equals(resolvedArgs, unresolvedArgs)
+                ? typeToResolve
+                : new ResolvedParameterizedType(typeToResolve, resolvedArgs);
+    }
+
+    /**
+     * Create instance with constructor.
+     *
+     * @param constructor const not null
+     * @param <T>         type of instance
+     * @return instance
+     */
+    public static <T> T createNoArgConstructorInstance(Constructor<T> constructor) {
+        Objects.requireNonNull(constructor);
+        try {
+            return constructor.newInstance();
+        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+            throw new JsonbException("Can't create instance", e);
+        }
+    }
+
+    /**
+     * Get default no argument constructor of the class.
+     *
+     * @param clazz    Class to get constructor from
+     * @param <T>      Class generic type
+     * @param required if true, throws an exception if the default constructor is missing.
+     *                 If false, returns null in that case
+     * @return the constructor of the class, or null. Depending on required.
+     */
+    public static <T> Constructor<T> getDefaultConstructor(Class<T> clazz, boolean required) {
+        Objects.requireNonNull(clazz);
+        return AccessController.doPrivileged((PrivilegedAction<Constructor<T>>) () -> {
+            try {
+                final Constructor<T> declaredConstructor = clazz.getDeclaredConstructor();
+                if (declaredConstructor.getModifiers() == Modifier.PROTECTED) {
+                    declaredConstructor.setAccessible(true);
+                }
+                return declaredConstructor;
+            } catch (NoSuchMethodException | RuntimeException e) {
+                if (required) {
+                    throw new JsonbException(Messages.getMessage(MessageKeys.NO_DEFAULT_CONSTRUCTOR, clazz), e);
+                }
+                return null;
+            }
+        });
+    }
+
+    /**
+     * For generic adapters like:
+     * <p>
+     * {@code
+     * interface ContainerAdapter<T> extends JsonbAdapter<Box<T>, Crate<T>>...;
+     * class IntegerBoxToCrateAdapter implements ContainerAdapter<Integer>...;
+     * }
+     * </p>
+     * We need to find a JsonbAdapter class which will hold basic generic type arguments,
+     * and resolve them if they are TypeVariables from there.
+     *
+     * @param classToSearch          class to resolve parameterized interface
+     * @param parameterizedInterface interface to search
+     * @return type of JsonbAdapter
+     */
+    public static ParameterizedType findParameterizedType(Class<?> classToSearch, Class<?> parameterizedInterface) {
+        Class current = classToSearch;
+        while (current != Object.class) {
+            for (Type currentInterface : current.getGenericInterfaces()) {
+                if (currentInterface instanceof ParameterizedType
+                        && parameterizedInterface.isAssignableFrom(
+                        ReflectionUtils.getRawType(((ParameterizedType) currentInterface).getRawType()))) {
+                    return (ParameterizedType) currentInterface;
+                }
+            }
+            current = current.getSuperclass();
+        }
+        throw new JsonbException(Messages.getMessage(MessageKeys.NON_PARAMETRIZED_TYPE, parameterizedInterface));
+    }
+
+    /**
+     * Check if type needs resolution. If type is a class or a parametrized type with all type arguments as classes
+     * than it is considered resolved. If any of types is type variable or wildcard type is not resolved.
+     *
+     * @param type Type to check.
+     * @return True if resolved
+     */
+    public static boolean isResolvedType(Type type) {
+        if (type instanceof ParameterizedType) {
+            for (Type typeArg : ((ParameterizedType) type).getActualTypeArguments()) {
+                if (!isResolvedType(typeArg)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return type instanceof Class<?>;
+    }
+
+    private static ParameterizedType findParameterizedSuperclass(Type type) {
+        if (type == null || type instanceof ParameterizedType) {
+            return (ParameterizedType) type;
+        }
+        if (!(type instanceof Class)) {
+            throw new JsonbException("Can't resolve ParameterizedType superclass for: " + type);
+        }
+        return findParameterizedSuperclass(((Class) type).getGenericSuperclass());
+    }
+
+    /**
+     * Resolves a wildcard most specific upper or lower bound.
+     *
+     * @param chain         Type.
+     * @param wildcardType Wildcard type.
+     * @return The most specific type.
+     */
+    private static Type resolveMostSpecificBound(List<Type> chain, WildcardType wildcardType, boolean warn) {
+        Class<?> result = Object.class;
+        for (Type upperBound : wildcardType.getUpperBounds()) {
+            result = getMostSpecificBound(chain, result, upperBound, warn);
+        }
+        for (Type lowerBound : wildcardType.getLowerBounds()) {
+            result = getMostSpecificBound(chain, result, lowerBound, warn);
+        }
+        return result;
+    }
+
+    private static Class<?> getMostSpecificBound(List<Type> chain, Class<?> result, Type bound, boolean warn) {
+        if (bound == Object.class) {
+            return result;
+        }
+        //if bound is type variable search recursively for wrapper generic expansion
+        Type resolvedBoundType = bound instanceof TypeVariable ? resolveType(chain, bound, warn) : bound;
+        Class<?> boundRawType = getRawType(resolvedBoundType);
+        //resolved class is a subclass of a result candidate
+        if (result.isAssignableFrom(boundRawType)) {
+            result = boundRawType;
+        }
+        return result;
+    }
+
+    public static final class GenericArrayTypeImpl implements GenericArrayType {
+        private final Type genericComponentType;
+
+        // private constructor enforces use of static factory
+        private GenericArrayTypeImpl(Type ct) {
+            genericComponentType = ct;
+        }
+
+        /**
+         * Returns a {@code Type} object representing the component type
+         * of this array.
+         *
+         * @return a {@code Type} object representing the component type
+         *     of this array
+         * @since 1.5
+         */
+        public Type getGenericComponentType() {
+            return genericComponentType; // return cached component type
+        }
+
+        public String toString() {
+            return getGenericComponentType().getTypeName() + "[]";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof GenericArrayType) {
+                GenericArrayType that = (GenericArrayType) o;
+
+                return Objects.equals(genericComponentType, that.getGenericComponentType());
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(genericComponentType);
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/ResolvedParameterizedType.java b/src/main/java/org/eclipse/yasson/internal/ResolvedParameterizedType.java
new file mode 100644
index 0000000..5be419e
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/ResolvedParameterizedType.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * {@link ParameterizedType} implementation containing array of resolved TypeVariable type args.
+ */
+class ResolvedParameterizedType implements ParameterizedType {
+
+    /**
+     * Original parameterized type.
+     */
+    private final ParameterizedType original;
+
+    /**
+     * Resolved args by runtime type.
+     */
+    private final Type[] resolvedTypeArgs;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param original         Original type.
+     * @param resolvedTypeArgs Resolved type arguments.
+     */
+    ResolvedParameterizedType(ParameterizedType original, Type[] resolvedTypeArgs) {
+        this.original = original;
+        this.resolvedTypeArgs = resolvedTypeArgs;
+    }
+
+    /**
+     * Type arguments with resolved TypeVariables.
+     *
+     * @return type args
+     */
+    @Override
+    public Type[] getActualTypeArguments() {
+        return resolvedTypeArgs;
+    }
+
+    @Override
+    public Type getRawType() {
+        return original.getRawType();
+    }
+
+    @Override
+    public Type getOwnerType() {
+        return original.getOwnerType();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(original.toString());
+        if (resolvedTypeArgs != null && resolvedTypeArgs.length > 0) {
+            sb.append(" resolved arguments: [");
+            for (Type typeArg : resolvedTypeArgs) {
+                sb.append(typeArg);
+            }
+            sb.append("]");
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || !(o instanceof ParameterizedType)) {
+            return false;
+        }
+        final ParameterizedType that = (ParameterizedType) o;
+        return this.getRawType().equals(that.getRawType())
+                && Objects.equals(this.getOwnerType(), that.getOwnerType())
+                && Arrays.equals(resolvedTypeArgs, that.getActualTypeArguments());
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(resolvedTypeArgs)
+                ^ (getOwnerType() == null ? 0 : getOwnerType().hashCode())
+                ^ (getRawType() == null ? 0 : getRawType().hashCode());
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/SerializationContextImpl.java b/src/main/java/org/eclipse/yasson/internal/SerializationContextImpl.java
new file mode 100644
index 0000000..1661dd4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/SerializationContextImpl.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020 Payara Foundation 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Type;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerationException;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+import org.eclipse.yasson.internal.serializer.ModelSerializer;
+
+/**
+ * JSONB marshaller. Created each time marshalling operation called.
+ */
+public class SerializationContextImpl extends ProcessingContext implements SerializationContext {
+
+    private static final Logger LOGGER = Logger.getLogger(SerializationContextImpl.class.getName());
+
+    /**
+     * Used to avoid StackOverflowError, when adapted / serialized object
+     * contains instance of its type inside it or when object has recursive reference.
+     */
+    private final Set<Object> currentlyProcessedObjects = new HashSet<>();
+
+    private final Type runtimeType;
+    private String key = null;
+    private boolean containerWithNulls = true;
+    private boolean root = true;
+
+    /**
+     * Creates Marshaller for generation to String.
+     *
+     * @param jsonbContext    Current context.
+     * @param rootRuntimeType Type of root object.
+     */
+    public SerializationContextImpl(JsonbContext jsonbContext, Type rootRuntimeType) {
+        super(jsonbContext);
+        this.runtimeType = rootRuntimeType;
+    }
+
+    /**
+     * Creates Marshaller for generation to String.
+     *
+     * @param jsonbContext Current context.
+     */
+    public SerializationContextImpl(JsonbContext jsonbContext) {
+        this(jsonbContext, null);
+    }
+
+    /**
+     * Set new current property key name.
+     *
+     * @param key key name
+     */
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    /**
+     * Current property key name.
+     *
+     * @return current property key name
+     */
+    public String getKey() {
+        return key;
+    }
+
+    /**
+     * Serialized value is a root value.
+     *
+     * @return is root value
+     */
+    public boolean isRoot() {
+        return root;
+    }
+
+    /**
+     * Set whether serialized value is root value.
+     *
+     * @param root is root value
+     */
+    public void setRoot(boolean root) {
+        this.root = root;
+    }
+
+    /**
+     * Value from this property is only used in {@link org.eclipse.yasson.internal.serializer.NullSerializer}.
+     * It should not be used anywhere else.
+     *
+     * @return if container supports nulls
+     */
+    public boolean isContainerWithNulls() {
+        return containerWithNulls;
+    }
+
+    /**
+     * Set if container supports null values.
+     *
+     * @param writeNulls should write nulls in container
+     */
+    public void setContainerWithNulls(boolean writeNulls) {
+        this.containerWithNulls = writeNulls;
+    }
+
+    /**
+     * Marshals given object to provided Writer or OutputStream.
+     *
+     * @param object        object to marshall
+     * @param jsonGenerator generator to use
+     * @param close         if generator should be closed
+     */
+    public void marshall(Object object, JsonGenerator jsonGenerator, boolean close) {
+        try {
+            serializeObject(object, jsonGenerator);
+        } catch (JsonbException e) {
+            throw e;
+        } catch (RuntimeException e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, e.getMessage()), e);
+        } finally {
+            try {
+                if (close) {
+                    jsonGenerator.close();
+                } else {
+                    jsonGenerator.flush();
+                }
+            } catch (JsonGenerationException jge) {
+                LOGGER.severe(jge.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Marshals given object to provided Writer or OutputStream.
+     * Closes the generator on completion.
+     *
+     * @param object        object to marshall
+     * @param jsonGenerator generator to use
+     */
+    public void marshall(Object object, JsonGenerator jsonGenerator) {
+        marshall(object, jsonGenerator, true);
+    }
+
+    /**
+     * Marshals given object to provided Writer or OutputStream.
+     * Leaves generator open for further interaction after completion.
+     *
+     * @param object        object to marshall
+     * @param jsonGenerator generator to use
+     */
+    public void marshallWithoutClose(Object object, JsonGenerator jsonGenerator) {
+        marshall(object, jsonGenerator, false);
+    }
+
+    @Override
+    public <T> void serialize(String key, T object, JsonGenerator generator) {
+        Objects.requireNonNull(key);
+        Objects.requireNonNull(object);
+        setKey(key);
+        serializeObject(object, generator);
+    }
+
+    @Override
+    public <T> void serialize(T object, JsonGenerator generator) {
+        Objects.requireNonNull(object);
+        serializeObject(object, generator);
+    }
+
+    /**
+     * Serializes root element.
+     *
+     * @param <T>       Root type
+     * @param root      Root.
+     * @param generator JSON generator.
+     */
+    public <T> void serializeObject(T root, JsonGenerator generator) {
+        Type type = determineSerializationType(root);
+        final ModelSerializer rootSerializer = getRootSerializer(type);
+        rootSerializer.serialize(root, generator, this);
+    }
+
+    private <T> Type determineSerializationType(T root) {
+        if (isRoot() && runtimeType != null) {
+            return runtimeType;
+        }
+        return root == null ? Object.class : root.getClass();
+    }
+
+    public ModelSerializer getRootSerializer(Type type) {
+        return getJsonbContext().getSerializationModelCreator().serializerChain(type, true, true);
+    }
+
+    /**
+     * Adds currently processed object to the {@link Set}.
+     *
+     * @param object processed object
+     * @return if object was added
+     */
+    public boolean addProcessedObject(Object object) {
+        return this.currentlyProcessedObjects.add(object);
+    }
+
+    /**
+     * Removes processed object from the {@link Set}.
+     *
+     * @param object processed object
+     * @return if object was removed
+     */
+    public boolean removeProcessedObject(Object object) {
+        return currentlyProcessedObjects.remove(object);
+    }
+
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/VariableTypeInheritanceSearch.java b/src/main/java/org/eclipse/yasson/internal/VariableTypeInheritanceSearch.java
new file mode 100644
index 0000000..263cc4e
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/VariableTypeInheritanceSearch.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Search for type variable in inheritance hierarchy and resolve if possible.
+ */
+class VariableTypeInheritanceSearch {
+
+    private final Deque<ParameterizedType> parameterizedSubclasses = new ArrayDeque<>();
+
+    /**
+     * Searches the hierarchy of classes to resolve a type variable. If typevar resolved value is another typevar redirection
+     * (propagated from wrapping class),
+     * this typevar is returned.
+     *
+     * <pre>
+     *
+     * Example 1: typevar is resolved
+     *
+     *
+     * class GenericClass &lt;T&gt; {
+     *     private T genericField;
+     * }
+     * class ConcreteClass extends GenericClass&lt;MyPojo&gt; {
+     *     //...
+     * }
+     *
+     * In above case when ConcreteClass type is passed as runtime type and &lt;T&gt; as type variable, T is resolved to MyPojo.
+     * </pre>
+     *
+     *
+     * <pre>
+     * Example 2: typevar is resolved to another propagated typevar
+     *
+     *
+     * class WrapperGenericClass&lt;X&gt; {
+     *     private GenericClass&lt;X&gt; propagatedGenericField
+     * }
+     *
+     * class AnotherClass extends WrapperGenericClass&lt;MyPojo&gt; {
+     * }
+     *
+     *
+     * In second case when GenericClass {@link ParameterizedType} is passed as runtime type and &lt;T&gt; as type variable,
+     * T is resolved to propagated &lt;X&gt; by WrapperGenericClass.
+     *
+     * Resolution on &lt;X&gt; must be performed thereafter with AnotherClass runtime type.
+     * </pre>
+     *
+     * @param typeToSearch runtime type to search for typevar in, not null
+     * @param typeVar      type variable to resolve, not null
+     * @return resolved runtime type, or type variable
+     */
+    Type searchParametrizedType(Type typeToSearch, TypeVariable<?> typeVar) {
+        ParameterizedType parameterizedType = findParameterizedSuperclass(typeToSearch);
+        if (parameterizedType == null) {
+            return null;
+        }
+        Type matchedGenericType = searchRuntimeTypeArgument(parameterizedType, typeVar);
+        if (matchedGenericType != null) {
+            return matchedGenericType;
+        }
+        parameterizedSubclasses.push(parameterizedType);
+        return searchParametrizedType(((Class) parameterizedType.getRawType()).getGenericSuperclass(), typeVar);
+    }
+
+    private Type checkSubclassRuntimeInfo(TypeVariable typeVar) {
+        if (parameterizedSubclasses.size() == 0) {
+            return typeVar;
+        }
+        ParameterizedType parametrizedSubclass = parameterizedSubclasses.pop();
+        return searchRuntimeTypeArgument(parametrizedSubclass, typeVar);
+    }
+
+    private Type searchRuntimeTypeArgument(ParameterizedType runtimeType, TypeVariable<?> typeVar) {
+        if (ReflectionUtils.getRawType(runtimeType) != typeVar.getGenericDeclaration()) {
+            return null;
+        }
+        TypeVariable[] bounds = typeVar.getGenericDeclaration().getTypeParameters();
+        for (int i = 0; i < bounds.length; i++) {
+            if (bounds[i].equals(typeVar)) {
+                Type matchedGenericType = runtimeType.getActualTypeArguments()[i];
+                //Propagated generic types to another generic classes
+                if (matchedGenericType instanceof TypeVariable<?>) {
+                    return checkSubclassRuntimeInfo((TypeVariable) matchedGenericType);
+                }
+                //found runtime matchedGenericType
+                return matchedGenericType;
+            }
+        }
+        return null;
+    }
+
+    private static ParameterizedType findParameterizedSuperclass(Type type) {
+        if (type == null || type instanceof ParameterizedType) {
+            return (ParameterizedType) type;
+        }
+        if (!(type instanceof Class)) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.RESOLVE_PARAMETRIZED_TYPE, type));
+        }
+        return findParameterizedSuperclass(((Class) type).getGenericSuperclass());
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/components/AbstractComponentBinding.java b/src/main/java/org/eclipse/yasson/internal/components/AbstractComponentBinding.java
new file mode 100644
index 0000000..f3be588
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/components/AbstractComponentBinding.java
@@ -0,0 +1,51 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.components;
+
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+/**
+ * Wrapper for user components, components, (de)serializer.
+ * Contains resolved binding type an component.
+ */
+public abstract class AbstractComponentBinding {
+
+    private final Type bindingType;
+
+    /**
+     * Creates info.
+     *
+     * @param bindingType type to which component is bound.
+     */
+    public AbstractComponentBinding(Type bindingType) {
+        Objects.requireNonNull(bindingType);
+        this.bindingType = bindingType;
+    }
+
+    /**
+     * Resolved binding type of a component.
+     *
+     * @return binding type
+     */
+    public Type getBindingType() {
+        return bindingType;
+    }
+
+    /**
+     * Class of user component.
+     *
+     * @return component class
+     */
+    public abstract Class<?> getComponentClass();
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/components/AdapterBinding.java b/src/main/java/org/eclipse/yasson/internal/components/AdapterBinding.java
new file mode 100644
index 0000000..16c57d7
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/components/AdapterBinding.java
@@ -0,0 +1,69 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.components;
+
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+/**
+ * Wrapper for JsonbAdapter generic information and an components itself.
+ */
+public class AdapterBinding extends AbstractComponentBinding {
+
+    private final Type toType;
+
+    private final JsonbAdapter<?, ?> adapter;
+
+    /**
+     * Adapter info with type to "adapt from", type to "adapt to" and an components itself.
+     *
+     * @param fromType from not null
+     * @param toType   to not null
+     * @param adapter  components not null
+     */
+    public AdapterBinding(Type fromType, Type toType, JsonbAdapter<?, ?> adapter) {
+        super(fromType);
+        Objects.requireNonNull(toType);
+        Objects.requireNonNull(adapter);
+        this.toType = toType;
+        this.adapter = adapter;
+    }
+
+    /**
+     * Represents a type to which to adapt into.
+     *
+     * During marshalling object property is adapted to this type and result is marshalled.
+     * During unmarshalling object is unmarshalled into this type first, than converted to field type and set.
+     *
+     * @return Type from which to adapt
+     */
+    public Type getToType() {
+        return toType;
+    }
+
+    /**
+     * Get actual components to adapt object value.
+     *
+     * @return components
+     */
+    public JsonbAdapter<?, ?> getAdapter() {
+        return adapter;
+    }
+
+    @Override
+    public Class<?> getComponentClass() {
+        return adapter.getClass();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/components/BeanManagerInstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/components/BeanManagerInstanceCreator.java
new file mode 100644
index 0000000..456015a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/components/BeanManagerInstanceCreator.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.components;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import jakarta.enterprise.context.spi.CreationalContext;
+import jakarta.enterprise.inject.spi.AnnotatedType;
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.enterprise.inject.spi.InjectionTarget;
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.JsonBinding;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+import org.eclipse.yasson.spi.JsonbComponentInstanceCreator;
+
+/**
+ * CDI instance manager.
+ * Instances are created and stored per instance of {@link JsonBinding}.
+ * Calling close on JsonBinding, cleans up Jsonb CDI instances and in case of "dependant" scope its dependencies.
+ *
+ * CDI API dependency is optional, this class is never referenced / loaded if CDI API is not resolvable.
+ */
+public class BeanManagerInstanceCreator implements JsonbComponentInstanceCreator {
+
+    private final BeanManager beanManager;
+
+    private final ConcurrentMap<Class<?>, CDIManagedBean<?>> injectionTargets = new ConcurrentHashMap<>();
+
+    /**
+     * Creates a new instance.
+     *
+     * @param beanManager Bean manager.
+     */
+    public BeanManagerInstanceCreator(Object beanManager) {
+        if (!(beanManager instanceof BeanManager)) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR,
+                                                         "beanManager instance should be of type '" + BeanManager.class + "'"));
+        }
+        this.beanManager = (BeanManager) beanManager;
+    }
+
+    /**
+     * Creates an instance of the CDI managed bean.
+     * Calls CDI API to inject into the bean.
+     *
+     * @param componentClass bean class to be instantiated.
+     * @return New instance of bean class with injected content.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getOrCreateComponent(Class<T> componentClass) {
+        return (T) injectionTargets.computeIfAbsent(componentClass, clazz -> {
+            final AnnotatedType<T> aType = beanManager.createAnnotatedType(componentClass);
+            final InjectionTarget<T> injectionTarget = beanManager.getInjectionTargetFactory(aType)
+                    .createInjectionTarget(null);
+            CreationalContext<T> creationalContext = beanManager.createCreationalContext(null);
+            final T beanInstance = injectionTarget.produce(creationalContext);
+            injectionTarget.inject(beanInstance, creationalContext);
+            injectionTarget.postConstruct(beanInstance);
+            return new CDIManagedBean(beanInstance, injectionTarget, creationalContext);
+        }).getInstance();
+    }
+
+    @Override
+    public void close() throws IOException {
+        injectionTargets.forEach((clazz, target) -> cleanupBean(target));
+        injectionTargets.clear();
+    }
+
+    private <T> void cleanupBean(CDIManagedBean<T> bean) {
+        bean.getInjectionTarget().preDestroy(bean.getInstance());
+        bean.getInjectionTarget().dispose(bean.getInstance());
+        bean.getCreationalContext().release();
+    }
+
+    /**
+     * Holder for bean instance and its injection target.
+     */
+    private static final class CDIManagedBean<T> {
+        private final T instance;
+        private final InjectionTarget<T> injectionTarget;
+        private final CreationalContext<T> creationalContext;
+
+        CDIManagedBean(T instance, InjectionTarget<T> injectionTarget, CreationalContext<T> creationalContext) {
+            this.instance = instance;
+            this.injectionTarget = injectionTarget;
+            this.creationalContext = creationalContext;
+        }
+
+        /**
+         * @return CDI InjectionTarget
+         */
+        private InjectionTarget<T> getInjectionTarget() {
+            return injectionTarget;
+        }
+
+        /**
+         * @return managed instance of a bean
+         */
+        private T getInstance() {
+            return instance;
+        }
+
+        /**
+         * @return creational context
+         */
+        private CreationalContext<T> getCreationalContext() {
+            return creationalContext;
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/components/ComponentBindings.java b/src/main/java/org/eclipse/yasson/internal/components/ComponentBindings.java
new file mode 100644
index 0000000..3033bd5
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/components/ComponentBindings.java
@@ -0,0 +1,93 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.components;
+
+import java.lang.reflect.Type;
+
+/**
+ * Wrapper holding singleton instances of user defined components - Adapters, (De)Serializers.
+ */
+public class ComponentBindings {
+
+    private final Type bindingType;
+
+    private final SerializerBinding serializer;
+
+    private final DeserializerBinding deserializer;
+
+    private final AdapterBinding adapterInfo;
+
+    /**
+     * Construct empty bindings for a given type.
+     *
+     * @param bindingType type components are bound to
+     */
+    public ComponentBindings(Type bindingType) {
+        this(bindingType, null, null, null);
+    }
+
+    /**
+     * Creates an instance and populates it with bindings for a given type.
+     *
+     * @param bindingType  Type components are bound to.
+     * @param serializer   Serializer.
+     * @param deserializer Deserializer.
+     * @param adapter      Adapter.
+     */
+    public ComponentBindings(Type bindingType,
+                             SerializerBinding serializer,
+                             DeserializerBinding deserializer,
+                             AdapterBinding adapter) {
+        this.bindingType = bindingType;
+        this.serializer = serializer;
+        this.deserializer = deserializer;
+        this.adapterInfo = adapter;
+    }
+
+    /**
+     * Type to which components are bound.
+     *
+     * @return Bound type.
+     */
+    public Type getBindingType() {
+        return bindingType;
+    }
+
+    /**
+     * Serializer if any.
+     *
+     * @return serializer
+     */
+    public SerializerBinding getSerializer() {
+        return serializer;
+    }
+
+    /**
+     * Deserializer if any.
+     *
+     * @return deserializer
+     */
+    public DeserializerBinding getDeserializer() {
+        return deserializer;
+    }
+
+    /**
+     * Adapter info if any.
+     *
+     * @return adapterInfo
+     */
+    public AdapterBinding getAdapterInfo() {
+        return adapterInfo;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/components/DefaultConstructorCreator.java b/src/main/java/org/eclipse/yasson/internal/components/DefaultConstructorCreator.java
new file mode 100644
index 0000000..ff949c0
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/components/DefaultConstructorCreator.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.components;
+
+import java.io.IOException;
+
+import org.eclipse.yasson.internal.InstanceCreator;
+import org.eclipse.yasson.spi.JsonbComponentInstanceCreator;
+
+/**
+ * Creates components instance with default constructor.
+ */
+public class DefaultConstructorCreator implements JsonbComponentInstanceCreator {
+
+    @Override
+    public <T> T getOrCreateComponent(Class<T> componentClass) {
+        return InstanceCreator.createInstance(componentClass);
+    }
+
+    @Override
+    public void close() throws IOException {
+
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/components/DeserializerBinding.java b/src/main/java/org/eclipse/yasson/internal/components/DeserializerBinding.java
new file mode 100644
index 0000000..b2ed21b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/components/DeserializerBinding.java
@@ -0,0 +1,52 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.components;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.bind.serializer.JsonbDeserializer;
+
+/**
+ * Component containing deserializer.
+ *
+ * @param <T> type of contained deserializer
+ */
+public class DeserializerBinding<T> extends AbstractComponentBinding {
+
+    private final JsonbDeserializer<T> jsonbDeserializer;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param bindingType       Binding type.
+     * @param jsonbDeserializer Deserializer.
+     */
+    public DeserializerBinding(Type bindingType, JsonbDeserializer<T> jsonbDeserializer) {
+        super(bindingType);
+        this.jsonbDeserializer = jsonbDeserializer;
+    }
+
+    /**
+     * Gets deserializer if any.
+     *
+     * @return Deserializer.
+     */
+    public JsonbDeserializer<T> getJsonbDeserializer() {
+        return jsonbDeserializer;
+    }
+
+    @Override
+    public Class<?> getComponentClass() {
+        return jsonbDeserializer.getClass();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/components/JsonbComponentInstanceCreatorFactory.java b/src/main/java/org/eclipse/yasson/internal/components/JsonbComponentInstanceCreatorFactory.java
new file mode 100644
index 0000000..92e6671
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/components/JsonbComponentInstanceCreatorFactory.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.components;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+import org.eclipse.yasson.spi.JsonbComponentInstanceCreator;
+
+/**
+ * Factory method for default Jsonb component instance creators.
+ */
+public class JsonbComponentInstanceCreatorFactory {
+
+    private static final Logger LOGGER = Logger.getLogger(JsonbComponentInstanceCreator.class.getName());
+
+    private JsonbComponentInstanceCreatorFactory() {
+        throw new IllegalStateException("This class should never be instantiated");
+    }
+
+    /**
+     * JNDI bean manager name.
+     */
+    public static final String BEAN_MANAGER_NAME = "java:comp/BeanManager";
+
+    /**
+     * Initial context class.
+     */
+    public static final String INITIAL_CONTEXT_CLASS = "javax.naming.InitialContext";
+    private static final String CDI_SPI_CLASS = "jakarta.enterprise.inject.spi.CDI";
+
+    /**
+     * First check a CDI provider, if available use those.
+     * Try to lookup in a JNDI if no provider is registered.
+     * If one of the above is found {@link BeanManagerInstanceCreator} is returned,
+     * or {@link DefaultConstructorCreator} otherwise.
+     *
+     * @return Component instance creator, either CDI or default constructor.
+     */
+    public static JsonbComponentInstanceCreator getComponentInstanceCreator() {
+        Object beanManager = getCdiBeanManager();
+        if (beanManager == null) {
+            beanManager = getJndiBeanManager();
+        }
+        if (beanManager == null) {
+            LOGGER.finest(Messages.getMessage(MessageKeys.BEAN_MANAGER_NOT_FOUND_USING_DEFAULT));
+            return new DefaultConstructorCreator();
+        }
+        return new BeanManagerInstanceCreator(beanManager);
+    }
+
+    /**
+     * Get bean manager with CDI api.
+     *
+     * @return bean manager instance or null if CDI API dependency is not available.
+     */
+    private static Object getCdiBeanManager() {
+        return AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
+            try {
+                return getBeanManager(() -> {
+                    Class<?> cdiClass = Class.forName(CDI_SPI_CLASS);
+                    Method current = cdiClass.getMethod("current");
+                    Method getBeanManager = cdiClass.getMethod("getBeanManager");
+                    Object cdiObject = current.invoke(cdiClass);
+                    if (cdiObject == null) {
+                        return null;
+                    }
+                    return getBeanManager.invoke(cdiObject);
+                });
+            } catch (ClassNotFoundException e) {
+                LOGGER.finest(Messages.getMessage(MessageKeys.NO_CDI_API_PROVIDER, CDI_SPI_CLASS));
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Get bean manager from JNDI context.
+     *
+     * @return bean manager instance or null if javax.naming is not available.
+     */
+    private static Object getJndiBeanManager() {
+        return AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
+            try {
+                return getBeanManager(() -> {
+                    Class<?> initialContextClass = Class.forName(INITIAL_CONTEXT_CLASS);
+                    Method lookupMethod = initialContextClass.getMethod("lookup", String.class);
+                    Constructor<?> initialContextConstructor = initialContextClass.getConstructor();
+                    Object initialContextObject = initialContextConstructor.newInstance();
+                    return lookupMethod.invoke(initialContextObject, BEAN_MANAGER_NAME);
+                });
+            } catch (ClassNotFoundException e) {
+                LOGGER.finest(Messages.getMessage(MessageKeys.NO_JNDI_ENVIRONMENT, INITIAL_CONTEXT_CLASS));
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Handles common invocation exceptions for getting bean manager reflectively.
+     *
+     * @return bean manager instance or null if javax.naming is not available or insufficient permissions.
+     */
+    private static Object getBeanManager(BeanManagerProvider command) throws ClassNotFoundException {
+        try {
+            return command.provide();
+        } catch (NoSuchMethodException | InstantiationException e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, e.getMessage()), e);
+        } catch (IllegalAccessException e) {
+            //insufficient permissions for reflective invocation, don't fail in this case
+            LOGGER.warning(e.getMessage());
+            LOGGER.warning(Messages.getMessage(MessageKeys.ILLEGAL_ACCESS, "lookup CDI bean manager"));
+            return null;
+        } catch (ClassNotFoundException e) {
+            throw e;
+        } catch (ReflectiveOperationException e) {
+            //likely no CDI container is running or bean manager JNDI lookup fails.
+            LOGGER.log(Level.FINEST, Messages.getMessage(MessageKeys.NO_CDI_ENVIRONMENT), e);
+            return null;
+        }
+    }
+
+    /**
+     * Provides CDI bean manager instance, declares all exceptions thrown with reflective calls.
+     */
+    private interface BeanManagerProvider {
+        Object provide() throws ReflectiveOperationException;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/components/SerializerBinding.java b/src/main/java/org/eclipse/yasson/internal/components/SerializerBinding.java
new file mode 100644
index 0000000..6baafde
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/components/SerializerBinding.java
@@ -0,0 +1,57 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.components;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+
+/**
+ * Binding for user Serializer component.
+ *
+ * @param <T> type of jsonb serializer
+ */
+public class SerializerBinding<T> extends AbstractComponentBinding {
+
+    private final JsonbSerializer<T> jsonbSerializer;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param bindingType     Generic type argument of serializer. Not null.
+     * @param jsonbSerializer Serializer. Can be null.
+     */
+    public SerializerBinding(Type bindingType, JsonbSerializer<T> jsonbSerializer) {
+        super(bindingType);
+        this.jsonbSerializer = jsonbSerializer;
+    }
+
+    /**
+     * Returns a serializer if any.
+     *
+     * @return Serializer.
+     */
+    public JsonbSerializer<T> getJsonbSerializer() {
+        return jsonbSerializer;
+    }
+
+    /**
+     * Class of user component.
+     *
+     * @return Component class.
+     */
+    @Override
+    public Class<?> getComponentClass() {
+        return jsonbSerializer.getClass();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/AdapterDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/AdapterDeserializer.java
new file mode 100644
index 0000000..b54e72a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/AdapterDeserializer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * User defined type adapter executor.
+ */
+class AdapterDeserializer implements ModelDeserializer<Object> {
+
+    private final JsonbAdapter<Object, Object> adapter;
+    private final AdapterBinding adapterBinding;
+    private final ModelDeserializer<Object> delegate;
+
+    @SuppressWarnings("unchecked")
+    AdapterDeserializer(AdapterBinding adapterBinding,
+                        ModelDeserializer<Object> delegate) {
+        this.adapterBinding = adapterBinding;
+        this.adapter = (JsonbAdapter<Object, Object>) adapterBinding.getAdapter();
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Object deserialize(Object value, DeserializationContextImpl context) {
+        try {
+            return delegate.deserialize(adapter.adaptFromJson(value), context);
+        } catch (Exception e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_EXCEPTION,
+                                                         adapterBinding.getBindingType(),
+                                                         adapterBinding.getToType(),
+                                                         adapterBinding.getAdapter().getClass()), e);
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/ArrayDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/ArrayDeserializer.java
new file mode 100644
index 0000000..2a85c97
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/ArrayDeserializer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Array container deserializer.
+ */
+class ArrayDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> delegate;
+
+    ArrayDeserializer(ModelDeserializer<JsonParser> delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Object deserialize(JsonParser parser, DeserializationContextImpl context) {
+        Collection<Object> collection = new ArrayList<>();
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            context.setLastValueEvent(next);
+            switch (next) {
+            case START_OBJECT:
+            case START_ARRAY:
+            case VALUE_STRING:
+            case VALUE_TRUE:
+            case VALUE_FALSE:
+            case VALUE_NUMBER:
+            case VALUE_NULL:
+                DeserializationContextImpl newContext = new DeserializationContextImpl(context);
+                collection.add(delegate.deserialize(parser, newContext));
+                break;
+            case END_ARRAY:
+                return collection;
+            default:
+                throw new JsonbException("Unexpected state: " + next);
+            }
+        }
+        return collection;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/ArrayInstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/deserializer/ArrayInstanceCreator.java
new file mode 100644
index 0000000..578f32f
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/ArrayInstanceCreator.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.lang.reflect.Array;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.Map;
+import java.util.function.Function;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.BinaryDataStrategy;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Creator of the array instance based upon the array type.
+ */
+abstract class ArrayInstanceCreator implements ModelDeserializer<JsonParser> {
+
+    private static final Map<Class<?>, Function<ModelDeserializer<JsonParser>, ArrayInstanceCreator>> CACHE;
+
+    static {
+        CACHE = Map.of(boolean[].class, BooleanArrayCreator::new,
+                       byte[].class, ByteArrayCreator::new,
+                       char[].class, CharArrayCreator::new,
+                       double[].class, DoubleArrayCreator::new,
+                       float[].class, FloatArrayCreator::new,
+                       int[].class, IntegerArrayCreator::new,
+                       long[].class, LongArrayCreator::new,
+                       short[].class, ShortArrayCreator::new);
+    }
+
+    private final ModelDeserializer<JsonParser> delegate;
+
+    private ArrayInstanceCreator(ModelDeserializer<JsonParser> delegate) {
+        this.delegate = delegate;
+    }
+
+    static ArrayInstanceCreator create(Class<?> arrayType, Class<?> componentClass, ModelDeserializer<JsonParser> delegate) {
+        if (CACHE.containsKey(arrayType)) {
+            return CACHE.get(arrayType).apply(delegate);
+        }
+        return new ObjectArrayCreator(delegate, componentClass);
+    }
+
+    static ModelDeserializer<JsonParser> createBase64Deserializer(String strategy,
+                                                                  ModelDeserializer<JsonParser> delegate) {
+        return new Base64ByteArray(strategy, delegate);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        Collection<Object> collection = (Collection<Object>) delegate.deserialize(value, context);
+        return resolveArrayInstance(collection);
+    }
+
+    protected abstract Object resolveArrayInstance(Collection<Object> collection);
+
+    private static final class IntegerArrayCreator extends ArrayInstanceCreator {
+
+        private IntegerArrayCreator(ModelDeserializer<JsonParser> delegate) {
+            super(delegate);
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            int[] intArray = new int[collection.size()];
+            int i = 0;
+            for (Object obj : collection) {
+                intArray[i] = (int) obj;
+                i++;
+            }
+            return intArray;
+        }
+
+    }
+
+    private static final class ByteArrayCreator extends ArrayInstanceCreator {
+
+        private ByteArrayCreator(ModelDeserializer<JsonParser> delegate) {
+            super(delegate);
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            byte[] byteArray = new byte[collection.size()];
+            int i = 0;
+            for (Object obj : collection) {
+                byteArray[i] = (byte) obj;
+                i++;
+            }
+            return byteArray;
+        }
+
+    }
+
+    private static final class ShortArrayCreator extends ArrayInstanceCreator {
+
+        private ShortArrayCreator(ModelDeserializer<JsonParser> delegate) {
+            super(delegate);
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            short[] shortArray = new short[collection.size()];
+            int i = 0;
+            for (Object obj : collection) {
+                shortArray[i] = (short) obj;
+                i++;
+            }
+            return shortArray;
+        }
+
+    }
+
+    private static final class LongArrayCreator extends ArrayInstanceCreator {
+
+        private LongArrayCreator(ModelDeserializer<JsonParser> delegate) {
+            super(delegate);
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            long[] longArray = new long[collection.size()];
+            int i = 0;
+            for (Object obj : collection) {
+                longArray[i] = (long) obj;
+                i++;
+            }
+            return longArray;
+        }
+
+    }
+
+    private static final class FloatArrayCreator extends ArrayInstanceCreator {
+
+        private FloatArrayCreator(ModelDeserializer<JsonParser> delegate) {
+            super(delegate);
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            float[] floatArray = new float[collection.size()];
+            int i = 0;
+            for (Object obj : collection) {
+                floatArray[i] = (float) obj;
+                i++;
+            }
+            return floatArray;
+        }
+
+    }
+
+    private static final class DoubleArrayCreator extends ArrayInstanceCreator {
+
+        private DoubleArrayCreator(ModelDeserializer<JsonParser> delegate) {
+            super(delegate);
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            double[] doubleArray = new double[collection.size()];
+            int i = 0;
+            for (Object obj : collection) {
+                doubleArray[i] = (double) obj;
+                i++;
+            }
+            return doubleArray;
+        }
+
+    }
+
+    private static final class BooleanArrayCreator extends ArrayInstanceCreator {
+
+        private BooleanArrayCreator(ModelDeserializer<JsonParser> delegate) {
+            super(delegate);
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            boolean[] booleanArray = new boolean[collection.size()];
+            int i = 0;
+            for (Object obj : collection) {
+                booleanArray[i] = (boolean) obj;
+                i++;
+            }
+            return booleanArray;
+        }
+
+    }
+
+    private static final class CharArrayCreator extends ArrayInstanceCreator {
+
+        private CharArrayCreator(ModelDeserializer<JsonParser> delegate) {
+            super(delegate);
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            char[] charArray = new char[collection.size()];
+            int i = 0;
+            for (Object obj : collection) {
+                charArray[i] = (char) obj;
+                i++;
+            }
+            return charArray;
+        }
+
+    }
+
+    private static final class ObjectArrayCreator extends ArrayInstanceCreator {
+
+        private final Class<?> componentClass;
+
+        private ObjectArrayCreator(ModelDeserializer<JsonParser> delegate, Class<?> componentClass) {
+            super(delegate);
+            this.componentClass = componentClass;
+        }
+
+        @Override
+        protected Object resolveArrayInstance(Collection<Object> collection) {
+            Object[] objectArray = (Object[]) Array.newInstance(componentClass, collection.size());
+            int i = 0;
+            for (Object obj : collection) {
+                objectArray[i] = obj;
+                i++;
+            }
+            return objectArray;
+        }
+
+    }
+
+    private static final class Base64ByteArray implements ModelDeserializer<JsonParser> {
+
+        private final Base64.Decoder decoder;
+        private final ModelDeserializer<JsonParser> delegate;
+
+        private Base64ByteArray(String strategy,
+                                ModelDeserializer<JsonParser> delegate) {
+            this.decoder = getDecoder(strategy);
+            this.delegate = delegate;
+        }
+
+        public Base64.Decoder getDecoder(String strategy) {
+            switch (strategy) {
+            case BinaryDataStrategy.BASE_64:
+                return Base64.getDecoder();
+            case BinaryDataStrategy.BASE_64_URL:
+                return Base64.getUrlDecoder();
+            default:
+                throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Invalid strategy: " + strategy));
+            }
+        }
+
+        @Override
+        public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+            return decoder.decode((String) delegate.deserialize(value, context));
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/CollectionDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/CollectionDeserializer.java
new file mode 100644
index 0000000..53a0dcb
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/CollectionDeserializer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.util.Collection;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Collection container deserializer.
+ */
+class CollectionDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> delegate;
+
+    CollectionDeserializer(ModelDeserializer<JsonParser> delegate) {
+        this.delegate = delegate;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object deserialize(JsonParser parser, DeserializationContextImpl context) {
+        Collection<Object> collection = (Collection<Object>) context.getInstance();
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            context.setLastValueEvent(next);
+            switch (next) {
+            case VALUE_NULL:
+            case START_OBJECT:
+            case START_ARRAY:
+            case VALUE_STRING:
+            case VALUE_TRUE:
+            case VALUE_FALSE:
+            case VALUE_NUMBER:
+                DeserializationContextImpl newContext = new DeserializationContextImpl(context);
+                collection.add(delegate.deserialize(parser, newContext));
+                break;
+            case END_ARRAY:
+                return collection;
+            default:
+                throw new JsonbException("Unexpected state: " + next);
+            }
+        }
+        return collection;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/CollectionInstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/deserializer/CollectionInstanceCreator.java
new file mode 100644
index 0000000..50d82b9
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/CollectionInstanceCreator.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.InstanceCreator;
+import org.eclipse.yasson.internal.ReflectionUtils;
+
+/**
+ * Collection instance creator.
+ */
+class CollectionInstanceCreator implements ModelDeserializer<JsonParser> {
+
+    private final CollectionDeserializer delegate;
+    private final Type type;
+    private final Class<?> clazz;
+    private final boolean isEnumSet;
+
+    CollectionInstanceCreator(CollectionDeserializer delegate, Type type) {
+        this.delegate = delegate;
+        this.clazz = implementationClass(ReflectionUtils.getRawType(type));
+        this.isEnumSet = EnumSet.class.isAssignableFrom(clazz);
+        this.type = isEnumSet ? ((ParameterizedType) type).getActualTypeArguments()[0] : type;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        Object instance;
+        if (isEnumSet) {
+            instance = EnumSet.noneOf((Class<Enum>) type);
+        } else {
+            instance = InstanceCreator.createInstance(clazz);
+        }
+        context.setInstance(instance);
+        return delegate.deserialize(value, context);
+    }
+
+    private Class<?> implementationClass(Class<?> type) {
+        if (type.isInterface()) {
+            return createInterfaceInstance(type);
+        }
+        return type;
+    }
+
+    private Class<?> createInterfaceInstance(Class<?> ifcType) {
+        if (List.class.isAssignableFrom(ifcType)) {
+            return ArrayList.class;
+        }
+        if (Set.class.isAssignableFrom(ifcType)) {
+            if (SortedSet.class.isAssignableFrom(ifcType)) {
+                return TreeSet.class;
+            }
+            return HashSet.class;
+        }
+        if (Queue.class.isAssignableFrom(ifcType)) {
+            return ArrayDeque.class;
+        }
+        if (Collection.class == ifcType) {
+            return ArrayList.class;
+        }
+        return ifcType;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/ContextSwitcher.java b/src/main/java/org/eclipse/yasson/internal/deserializer/ContextSwitcher.java
new file mode 100644
index 0000000..462da57
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/ContextSwitcher.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer which creates new deserialization context and invokes delegate with it.
+ */
+class ContextSwitcher implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<Object> delegate;
+    private final ModelDeserializer<JsonParser> modelDeserializer;
+
+    ContextSwitcher(ModelDeserializer<Object> delegate,
+                    ModelDeserializer<JsonParser> modelDeserializer) {
+        this.delegate = delegate;
+        this.modelDeserializer = modelDeserializer;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        DeserializationContextImpl ctx = new DeserializationContextImpl(context);
+        Object returnedValue = delegate.deserialize(modelDeserializer.deserialize(value, ctx), context);
+        context.setLastValueEvent(ctx.getLastValueEvent());
+        return returnedValue;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/CyclicReferenceDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/CyclicReferenceDeserializer.java
new file mode 100644
index 0000000..f8ce5b4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/CyclicReferenceDeserializer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserialization solution for cyclic references.
+ */
+class CyclicReferenceDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final Type type;
+    private ModelDeserializer<JsonParser> delegate;
+
+    CyclicReferenceDeserializer(Type type) {
+        this.type = type;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        if (delegate == null) {
+            delegate = context.getJsonbContext().getChainModelCreator().deserializerChain(type);
+        }
+        return delegate.deserialize(value, context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/DefaultObjectInstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/deserializer/DefaultObjectInstanceCreator.java
new file mode 100644
index 0000000..822da18
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/DefaultObjectInstanceCreator.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.lang.reflect.Constructor;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.ClassMultiReleaseExtension;
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.ReflectionUtils;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Creator of the class instance with the default constructor.
+ */
+class DefaultObjectInstanceCreator implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> delegate;
+    private final Constructor<?> defaultConstructor;
+    private final JsonbException exception;
+
+    DefaultObjectInstanceCreator(ModelDeserializer<JsonParser> delegate,
+                                 Class<?> clazz,
+                                 Constructor<?> defaultConstructor) {
+        this.delegate = delegate;
+        this.defaultConstructor = defaultConstructor;
+        if (clazz.isInterface()) {
+            this.exception = new JsonbException(Messages.getMessage(MessageKeys.INFER_TYPE_FOR_UNMARSHALL, clazz.getName()));
+        } else if (defaultConstructor == null) {
+            this.exception = ClassMultiReleaseExtension.exceptionToThrow(clazz)
+                    .orElse(new JsonbException(Messages.getMessage(MessageKeys.NO_DEFAULT_CONSTRUCTOR, clazz)));
+        } else {
+            this.exception = null;
+        }
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        if (exception != null) {
+            throw exception;
+        }
+        Object instance = ReflectionUtils.createNoArgConstructorInstance(defaultConstructor);
+        context.setInstance(instance);
+        return delegate.deserialize(value, context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/DeferredDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/DeferredDeserializer.java
new file mode 100644
index 0000000..9d87bfd
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/DeferredDeserializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deferred deserializer used for postponed value setting. Such as when {@link jakarta.json.bind.annotation.JsonbCreator}
+ * is used.
+ */
+class DeferredDeserializer implements ModelDeserializer<Object> {
+
+    private final ModelDeserializer<Object> delegate;
+
+    DeferredDeserializer(ModelDeserializer<Object> delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Object deserialize(Object value, DeserializationContextImpl context) {
+        context.getDeferredDeserializers().add(() -> delegate.deserialize(value, context));
+        return value;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java b/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java
new file mode 100644
index 0000000..0ac3f98
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/DeserializationModelCreator.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 2021, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.BinaryDataStrategy;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.JsonbConfigProperties;
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+import org.eclipse.yasson.internal.ReflectionUtils;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.components.DeserializerBinding;
+import org.eclipse.yasson.internal.deserializer.types.TypeDeserializers;
+import org.eclipse.yasson.internal.model.ClassModel;
+import org.eclipse.yasson.internal.model.CreatorModel;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.model.PropertyModel;
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+import org.eclipse.yasson.internal.model.customization.ComponentBoundCustomization;
+import org.eclipse.yasson.internal.model.customization.Customization;
+import org.eclipse.yasson.internal.model.customization.PropertyCustomization;
+import org.eclipse.yasson.internal.model.customization.TypeInheritanceConfiguration;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import static jakarta.json.bind.JsonbConfig.PROPERTY_NAMING_STRATEGY;
+import static jakarta.json.stream.JsonParser.Event;
+
+/**
+ * Creator of the deserialization models for deserialized types.
+ * <br>
+ * This class servers also as a cache for all previously created model deserializers.
+ */
+public class DeserializationModelCreator {
+
+    private static final ModelDeserializer<Object> NULL_PROVIDER = (value, context) -> null;
+    private static final Map<Class<?>, ModelDeserializer<Object>> DEFAULT_CREATOR_VALUES;
+    private static final Set<JsonParser.Event> MAP_KEY_EVENTS = new HashSet<>();
+
+    static {
+        MAP_KEY_EVENTS.add(Event.KEY_NAME);
+        MAP_KEY_EVENTS.addAll(PositionChecker.Checker.VALUES.getEvents());
+
+        Map<Class<?>, ModelDeserializer<Object>> tmpValuesMap = new HashMap<>();
+
+        tmpValuesMap.put(byte.class, (value, context) -> (byte) 0);
+        tmpValuesMap.put(short.class, (value, context) -> (short) 0);
+        tmpValuesMap.put(int.class, (value, context) -> 0);
+        tmpValuesMap.put(long.class, (value, context) -> 0L);
+        tmpValuesMap.put(float.class, (value, context) -> 0.0F);
+        tmpValuesMap.put(double.class, (value, context) -> 0.0);
+        tmpValuesMap.put(char.class, (value, context) -> '\u0000');
+        tmpValuesMap.put(boolean.class, (value, context) -> false);
+        tmpValuesMap.put(Optional.class, (value, context) -> Optional.empty());
+        tmpValuesMap.put(OptionalInt.class, (value, context) -> OptionalInt.empty());
+        tmpValuesMap.put(OptionalLong.class, (value, context) -> OptionalLong.empty());
+        tmpValuesMap.put(OptionalDouble.class, (value, context) -> OptionalDouble.empty());
+
+        DEFAULT_CREATOR_VALUES = Map.copyOf(tmpValuesMap);
+    }
+
+    private final Map<CachedItem, ModelDeserializer<JsonParser>> models = new ConcurrentHashMap<>();
+
+    private final JsonbContext jsonbContext;
+    private final Map<Class<?>, Class<?>> userTypeMapping;
+
+    /**
+     * Create new instance.
+     *
+     * @param jsonbContext jsonb context
+     */
+    public DeserializationModelCreator(JsonbContext jsonbContext) {
+        this.jsonbContext = jsonbContext;
+        this.userTypeMapping = jsonbContext.getConfigProperties().getUserTypeMapping();
+    }
+
+    /**
+     * Starts deserializer creation process.
+     *
+     * @param type type the deserializer is created for
+     * @return created deserializer
+     */
+    public ModelDeserializer<JsonParser> deserializerChain(Type type) {
+        LinkedList<Type> chain = new LinkedList<>();
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(type));
+        return deserializerChain(chain, type, classModel.getClassCustomization(), classModel);
+    }
+
+    private ModelDeserializer<JsonParser> deserializerChain(LinkedList<Type> chain,
+                                                            Type type,
+                                                            Customization propertyCustomization,
+                                                            ClassModel classModel) {
+        if (chain.contains(type)) {
+            return new CyclicReferenceDeserializer(type);
+        }
+        try {
+            chain.add(type);
+            return deserializerChainInternal(chain, type, propertyCustomization, classModel);
+        } finally {
+            chain.removeLast();
+        }
+    }
+
+    private ModelDeserializer<JsonParser> deserializerChainInternal(LinkedList<Type> chain,
+                                                                    Type type,
+                                                                    Customization propertyCustomization,
+                                                                    ClassModel classModel) {
+        Class<?> rawType = classModel.getType();
+        CachedItem cachedItem = createCachedItem(type, propertyCustomization);
+        if (models.containsKey(cachedItem)) {
+            return models.get(cachedItem);
+        } else if (userTypeMapping.containsKey(rawType)) {
+            Class<?> userTypeRaw = userTypeMapping.get(rawType);
+            ModelDeserializer<JsonParser> deserializer = deserializerChain(userTypeRaw);
+            models.put(cachedItem, deserializer);
+            return deserializer;
+        }
+        Optional<AdapterBinding> adapterBinding = adapterBinding(type, (ComponentBoundCustomization) propertyCustomization);
+        if (adapterBinding.isPresent()) {
+            AdapterBinding adapter = adapterBinding.get();
+            Class<?> toType = ReflectionUtils.getRawType(adapter.getToType());
+            ClassModel targetModel = jsonbContext.getMappingContext().getOrCreateClassModel(toType);
+            ModelDeserializer<JsonParser> typeDeserializer = typeDeserializer(toType,
+                                                                              targetModel.getClassCustomization(),
+                                                                              JustReturn.instance());
+            if (typeDeserializer == null) {
+                typeDeserializer = deserializerChain(adapter.getToType());
+            }
+            ModelDeserializer<JsonParser> targetAdapterModel = typeDeserializer;
+            AdapterDeserializer adapterDeserializer = new AdapterDeserializer(adapter, JustReturn.instance());
+            ModelDeserializer<JsonParser> adapterDeser = (parser, context) -> {
+                Object fromJson = targetAdapterModel.deserialize(parser, context);
+                return adapterDeserializer.deserialize(fromJson, context);
+            };
+            models.put(cachedItem, adapterDeser);
+            return adapterDeser;
+        }
+        ModelDeserializer<JsonParser> typeDeserializer = typeDeserializer(rawType,
+                                                                          propertyCustomization,
+                                                                          JustReturn.instance());
+        if (typeDeserializer != null) {
+            models.put(cachedItem, typeDeserializer);
+            return typeDeserializer;
+        }
+        if (Collection.class.isAssignableFrom(rawType)) {
+            return createCollectionDeserializer(cachedItem, rawType, chain, propertyCustomization);
+        } else if (Map.class.isAssignableFrom(rawType)) {
+            return createMapDeserializer(cachedItem, rawType, chain, propertyCustomization);
+        } else if (rawType.isArray()) {
+            return createArrayDeserializer(cachedItem, rawType, chain, propertyCustomization);
+        } else if (type instanceof GenericArrayType) {
+            return createGenericArray(cachedItem, rawType, chain, propertyCustomization);
+        } else if (Optional.class.isAssignableFrom(rawType)) {
+            return createOptionalDeserializer(chain, type, propertyCustomization, cachedItem);
+        } else {
+            return createObjectDeserializer(chain, type, propertyCustomization, classModel, rawType, cachedItem);
+        }
+    }
+
+    private ModelDeserializer<JsonParser> createObjectDeserializer(LinkedList<Type> chain,
+                                                                   Type type,
+                                                                   Customization propertyCustomization,
+                                                                   ClassModel classModel,
+                                                                   Class<?> rawType,
+                                                                   CachedItem cachedItem) {
+        ClassCustomization classCustomization = classModel.getClassCustomization();
+        Optional<DeserializerBinding<?>> deserializerBinding = userDeserializer(type,
+                                                                                (ComponentBoundCustomization) propertyCustomization);
+        if (deserializerBinding.isPresent()) {
+            UserDefinedDeserializer user = new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(),
+                                                                       JustReturn.instance(), type, classCustomization);
+            models.put(cachedItem, user);
+            return user;
+        }
+        JsonbCreator creator = classCustomization.getCreator();
+        boolean hasCreator = creator != null;
+        List<String> params = hasCreator ? creatorParamsList(creator) : Collections.emptyList();
+        Function<String, String> renamer = propertyRenamer();
+        Map<String, ModelDeserializer<JsonParser>> processors = new LinkedHashMap<>();
+        Map<String, ModelDeserializer<Object>> defaultCreatorValues = new HashMap<>();
+        for (PropertyModel propertyModel : classModel.getSortedProperties()) {
+            if (!propertyModel.isWritable() || params.contains(propertyModel.getReadName())) {
+                continue;
+            }
+            ModelDeserializer<JsonParser> modelDeserializer = memberTypeProcessor(chain, propertyModel, hasCreator);
+            processors.put(renamer.apply(propertyModel.getReadName()), modelDeserializer);
+        }
+        for (String s : params) {
+            CreatorModel creatorModel = creator.findByName(s);
+            ModelDeserializer<JsonParser> modelDeserializer = typeProcessor(chain,
+                                                                            creatorModel.getType(),
+                                                                            creatorModel.getCustomization(),
+                                                                            JustReturn.instance());
+            String parameterName = renamer.apply(creatorModel.getName());
+            processors.put(parameterName, modelDeserializer);
+            if (creatorModel.getCustomization().isRequired()) {
+                defaultCreatorValues.put(parameterName, new RequiredCreatorParameter(parameterName));
+            } else {
+                Class<?> rawParamType = ReflectionUtils.getRawType(creatorModel.getType());
+                defaultCreatorValues.put(parameterName, DEFAULT_CREATOR_VALUES.getOrDefault(rawParamType, NULL_PROVIDER));
+            }
+        }
+        ModelDeserializer<JsonParser> instanceCreator;
+        TypeInheritanceConfiguration typeInheritanceConfiguration = classCustomization.getPolymorphismConfig();
+        Set<String> ignoredProperties = collectIgnoredProperties(typeInheritanceConfiguration);
+        boolean failOnUnknownProperties = jsonbContext.getConfigProperties().getConfigFailOnUnknownProperties();
+        if (hasCreator) {
+            instanceCreator = new JsonbCreatorDeserializer(processors, defaultCreatorValues, creator, rawType, renamer,
+                                                           failOnUnknownProperties, ignoredProperties);
+        } else {
+            ModelDeserializer<JsonParser> typeWrapper = new ObjectDeserializer(processors, renamer, rawType,
+                                                                               failOnUnknownProperties, ignoredProperties);
+            instanceCreator = new DefaultObjectInstanceCreator(typeWrapper, rawType,
+                                                               classModel.getDefaultConstructor());
+        }
+        PositionChecker positionChecker = new PositionChecker(instanceCreator, rawType, Event.START_OBJECT);
+        if (typeInheritanceConfiguration != null && !typeInheritanceConfiguration.isInherited()) {
+            instanceCreator = new InheritanceInstanceCreator(rawType, this, typeInheritanceConfiguration, positionChecker);
+            positionChecker = new PositionChecker(instanceCreator, rawType, Event.START_OBJECT);
+        }
+        ModelDeserializer<JsonParser> nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance());
+        models.put(cachedItem, nullChecker);
+        return nullChecker;
+    }
+
+    private ModelDeserializer<JsonParser> createCollectionDeserializer(CachedItem cachedItem,
+                                                                       Class<?> rawType,
+                                                                       LinkedList<Type> chain,
+                                                                       Customization propertyCustomization) {
+        Type type = cachedItem.type;
+        Type colType = type instanceof ParameterizedType
+                ? ((ParameterizedType) type).getActualTypeArguments()[0]
+                : Object.class;
+        colType = ReflectionUtils.resolveType(chain, colType);
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(ReflectionUtils.getRawType(colType));
+        ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain,
+                                                                    colType,
+                                                                    classModel.getClassCustomization(),
+                                                                    JustReturn.instance());
+        CollectionDeserializer collectionDeserializer = new CollectionDeserializer(typeProcessor);
+        CollectionInstanceCreator instanceDeserializer = new CollectionInstanceCreator(collectionDeserializer, type);
+        PositionChecker positionChecker = new PositionChecker(instanceDeserializer, rawType, Event.START_ARRAY);
+        NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance());
+        models.put(cachedItem, nullChecker);
+        return nullChecker;
+    }
+
+    private ModelDeserializer<JsonParser> createMapDeserializer(CachedItem cachedItem,
+                                                                Class<?> rawType,
+                                                                LinkedList<Type> chain,
+                                                                Customization propertyCustomization) {
+        Type type = cachedItem.type;
+        Type keyType = type instanceof ParameterizedType
+                ? ((ParameterizedType) type).getActualTypeArguments()[0]
+                : Object.class;
+        Type valueType = type instanceof ParameterizedType
+                ? ((ParameterizedType) type).getActualTypeArguments()[1]
+                : Object.class;
+        ModelDeserializer<JsonParser> keyProcessor = typeProcessor(chain,
+                                                                   keyType,
+                                                                   ClassCustomization.empty(),
+                                                                   JustReturn.instance(),
+                                                                   MAP_KEY_EVENTS);
+        ModelDeserializer<JsonParser> valueProcessor = typeProcessor(chain,
+                                                                     valueType,
+                                                                     propertyCustomization,
+                                                                     JustReturn.instance());
+
+        MapDeserializer mapDeserializer = new MapDeserializer(keyProcessor, valueProcessor);
+        MapInstanceCreator mapInstanceCreator = new MapInstanceCreator(mapDeserializer,
+                                                                       jsonbContext.getConfigProperties(),
+                                                                       rawType);
+        PositionChecker positionChecker = new PositionChecker(mapInstanceCreator, rawType, PositionChecker.Checker.CONTAINER);
+        NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance());
+        models.put(cachedItem, nullChecker);
+        return nullChecker;
+    }
+
+    private ModelDeserializer<JsonParser> createArrayDeserializer(CachedItem cachedItem,
+                                                                  Class<?> rawType,
+                                                                  LinkedList<Type> chain,
+                                                                  Customization propertyCustomization) {
+        JsonbConfigProperties configProperties = jsonbContext.getConfigProperties();
+        if (rawType.equals(byte[].class) && !configProperties.getBinaryDataStrategy().equals(BinaryDataStrategy.BYTE)) {
+            String strategy = configProperties.getBinaryDataStrategy();
+            ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain,
+                                                                        String.class,
+                                                                        propertyCustomization,
+                                                                        JustReturn.instance());
+            ModelDeserializer<JsonParser> base64Deserializer = ArrayInstanceCreator.createBase64Deserializer(strategy,
+                                                                                                             typeProcessor);
+            NullCheckDeserializer nullChecker = new NullCheckDeserializer(base64Deserializer, JustReturn.instance());
+            models.put(cachedItem, nullChecker);
+            return nullChecker;
+        }
+        Class<?> arrayType = rawType.getComponentType();
+        ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain,
+                                                                    arrayType,
+                                                                    propertyCustomization,
+                                                                    JustReturn.instance());
+        return createArrayCommonDeserializer(cachedItem, rawType, arrayType, typeProcessor);
+    }
+
+    private ModelDeserializer<JsonParser> createGenericArray(CachedItem cachedItem,
+                                                             Class<?> rawType,
+                                                             LinkedList<Type> chain,
+                                                             Customization propertyCustomization) {
+        GenericArrayType type = (GenericArrayType) cachedItem.type;
+        Class<?> component = ReflectionUtils.getRawType(type.getGenericComponentType());
+        ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain,
+                                                                    type.getGenericComponentType(),
+                                                                    propertyCustomization,
+                                                                    JustReturn.instance());
+        return createArrayCommonDeserializer(cachedItem, rawType, component, typeProcessor);
+    }
+
+    private ModelDeserializer<JsonParser> createArrayCommonDeserializer(CachedItem cachedItem,
+                                                                        Class<?> rawType,
+                                                                        Class<?> component,
+                                                                        ModelDeserializer<JsonParser> typeProcessor) {
+        ArrayDeserializer arrayDeserializer = new ArrayDeserializer(typeProcessor);
+        ArrayInstanceCreator arrayInstanceCreator = ArrayInstanceCreator.create(rawType, component, arrayDeserializer);
+        PositionChecker positionChecker = new PositionChecker(arrayInstanceCreator, rawType, Event.START_ARRAY);
+        NullCheckDeserializer nullChecker = new NullCheckDeserializer(positionChecker, JustReturn.instance());
+        models.put(cachedItem, nullChecker);
+        return nullChecker;
+    }
+
+    private OptionalDeserializer createOptionalDeserializer(LinkedList<Type> chain,
+                                                            Type type,
+                                                            Customization propertyCustomization,
+                                                            CachedItem cachedItem) {
+        Type colType = type instanceof ParameterizedType
+                ? ((ParameterizedType) type).getActualTypeArguments()[0]
+                : Object.class;
+        ModelDeserializer<JsonParser> typeProcessor = typeProcessor(chain, colType, propertyCustomization, JustReturn.instance());
+        OptionalDeserializer optionalDeserializer = new OptionalDeserializer(typeProcessor, JustReturn.instance());
+        models.put(cachedItem, optionalDeserializer);
+        return optionalDeserializer;
+    }
+
+    private Set<String> collectIgnoredProperties(TypeInheritanceConfiguration typeInheritanceConfiguration) {
+        Set<String> ignoredProperties = new HashSet<>();
+        if (typeInheritanceConfiguration != null) {
+            TypeInheritanceConfiguration current = typeInheritanceConfiguration;
+            while (current != null) {
+                ignoredProperties.add(current.getFieldName());
+                current = current.getParentConfig();
+            }
+        }
+        return ignoredProperties;
+    }
+
+    private Function<String, String> propertyRenamer() {
+        boolean isCaseInsensitive = jsonbContext.getConfig()
+                .getProperty(PROPERTY_NAMING_STRATEGY)
+                .filter(prop -> prop.equals(PropertyNamingStrategy.CASE_INSENSITIVE))
+                .isPresent();
+
+        return isCaseInsensitive
+                ? String::toLowerCase
+                : value -> value;
+    }
+
+    private Optional<AdapterBinding> adapterBinding(Type type, ComponentBoundCustomization classCustomization) {
+        return jsonbContext.getComponentMatcher().getDeserializeAdapterBinding(type, classCustomization);
+    }
+
+    private Optional<DeserializerBinding<?>> userDeserializer(Type type, ComponentBoundCustomization classCustomization) {
+        return jsonbContext.getComponentMatcher().getDeserializerBinding(type, classCustomization);
+    }
+
+    private List<String> creatorParamsList(JsonbCreator creator) {
+        return Arrays.stream(creator.getParams()).map(CreatorModel::getName).collect(Collectors.toList());
+    }
+
+    private ModelDeserializer<JsonParser> memberTypeProcessor(LinkedList<Type> chain,
+                                                              PropertyModel propertyModel,
+                                                              boolean hasCreator) {
+        ModelDeserializer<Object> memberDeserializer;
+        Type type = propertyModel.getPropertyDeserializationType();
+        memberDeserializer = new ValueSetterDeserializer(propertyModel.getSetValueHandle());
+        if (hasCreator) {
+            memberDeserializer = new DeferredDeserializer(memberDeserializer);
+        }
+        return typeProcessor(chain, type, propertyModel.getCustomization(), memberDeserializer);
+    }
+
+    private ModelDeserializer<JsonParser> typeProcessor(LinkedList<Type> chain,
+                                                        Type type,
+                                                        Customization customization,
+                                                        ModelDeserializer<Object> memberDeserializer) {
+        return typeProcessor(chain, type, customization, memberDeserializer, PositionChecker.Checker.VALUES.getEvents());
+    }
+
+    private ModelDeserializer<JsonParser> typeProcessor(LinkedList<Type> chain,
+                                                        Type type,
+                                                        Customization customization,
+                                                        ModelDeserializer<Object> memberDeserializer,
+                                                        Set<Event> events) {
+        Type resolved = ReflectionUtils.resolveType(chain, type);
+        Class<?> rawType = ReflectionUtils.getRawType(resolved);
+        Optional<DeserializerBinding<?>> deserializerBinding = userDeserializer(resolved,
+                                                                                (ComponentBoundCustomization) customization);
+        if (deserializerBinding.isPresent()) {
+            //TODO remove or not? fix for deserializer cycle
+            //            ModelDeserializer<JsonParser> exactType = createNewChain(chain, memberDeserializer, rawType,
+            //            resolved, customization);
+            //            return new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(),
+            //                                               exactType,
+            //                                               memberDeserializer,
+            //                                               resolved,
+            //                                               customization);
+            return new UserDefinedDeserializer(deserializerBinding.get().getJsonbDeserializer(),
+                                               memberDeserializer,
+                                               resolved,
+                                               customization);
+        }
+        Optional<AdapterBinding> adapterBinding = adapterBinding(resolved, (ComponentBoundCustomization) customization);
+        if (adapterBinding.isPresent()) {
+            AdapterBinding adapter = adapterBinding.get();
+            ModelDeserializer<JsonParser> typeDeserializer = typeDeserializer(ReflectionUtils.getRawType(adapter.getToType()),
+                                                                              customization,
+                                                                              JustReturn.instance(), events);
+            if (typeDeserializer == null) {
+                typeDeserializer = deserializerChain(adapter.getToType());
+            }
+            ModelDeserializer<JsonParser> targetAdapterModel = typeDeserializer;
+
+            AdapterDeserializer adapterDeserializer = new AdapterDeserializer(adapter, memberDeserializer);
+            return (parser, context) -> {
+                DeserializationContextImpl newContext = new DeserializationContextImpl(context);
+                Object fromJson = targetAdapterModel.deserialize(parser, newContext);
+                return adapterDeserializer.deserialize(fromJson, context);
+            };
+        }
+        ModelDeserializer<JsonParser> typeDeserializer = typeDeserializer(rawType, customization, memberDeserializer, events);
+        if (typeDeserializer == null) {
+            Class<?> implClass = resolveImplClass(rawType, customization);
+            return createNewChain(chain, memberDeserializer, implClass, resolved, customization);
+        }
+        return typeDeserializer;
+    }
+
+    private ModelDeserializer<JsonParser> createNewChain(LinkedList<Type> chain,
+                                                         ModelDeserializer<Object> memberDeserializer,
+                                                         Class<?> rawType,
+                                                         Type type,
+                                                         Customization propertyCustomization) {
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawType);
+        ModelDeserializer<JsonParser> modelDeserializer = deserializerChain(chain, type, propertyCustomization, classModel);
+        return new ContextSwitcher(memberDeserializer, modelDeserializer);
+    }
+
+    private ModelDeserializer<JsonParser> typeDeserializer(Class<?> rawType,
+                                                           Customization customization,
+                                                           ModelDeserializer<Object> delegate) {
+        return typeDeserializer(rawType, customization, delegate, PositionChecker.Checker.VALUES.getEvents());
+    }
+
+    private ModelDeserializer<JsonParser> typeDeserializer(Class<?> rawType,
+                                                           Customization customization,
+                                                           ModelDeserializer<Object> delegate,
+                                                           Set<JsonParser.Event> events) {
+        return TypeDeserializers
+                .getTypeDeserializer(rawType, customization, jsonbContext.getConfigProperties(), delegate, events);
+    }
+
+    private Class<?> resolveImplClass(Class<?> rawType, Customization customization) {
+        if (rawType.isInterface()) {
+            Class<?> implementationClass = null;
+            //annotation
+            if (customization instanceof PropertyCustomization) {
+                implementationClass = ((PropertyCustomization) customization).getImplementationClass();
+            }
+            //JsonbConfig
+            if (implementationClass == null) {
+                implementationClass = jsonbContext.getConfigProperties().getUserTypeMapping().get(rawType);
+            }
+            if (implementationClass != null) {
+                if (!rawType.isAssignableFrom(implementationClass)) {
+                    throw new JsonbException(Messages.getMessage(MessageKeys.IMPL_CLASS_INCOMPATIBLE,
+                                                                 implementationClass,
+                                                                 rawType));
+                }
+                return implementationClass;
+            }
+        }
+        return rawType;
+    }
+
+    private CachedItem createCachedItem(Type type, Customization customization) {
+        return new CachedItem(type, customization.getDeserializeNumberFormatter(), customization.getDeserializeDateFormatter());
+    }
+
+    private static final class CachedItem {
+
+        private final Type type;
+        private final JsonbNumberFormatter numberFormatter;
+        private final JsonbDateFormatter dateFormatter;
+
+        CachedItem(Type type, JsonbNumberFormatter numberFormatter, JsonbDateFormatter dateFormatter) {
+            this.type = type;
+            this.numberFormatter = numberFormatter;
+            this.dateFormatter = dateFormatter;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            CachedItem that = (CachedItem) o;
+            return Objects.equals(type, that.type)
+                    && Objects.equals(numberFormatter, that.numberFormatter)
+                    && Objects.equals(dateFormatter, that.dateFormatter);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type, numberFormatter, dateFormatter);
+        }
+
+        @Override
+        public String toString() {
+            return "CachedItem{"
+                    + "type=" + type
+                    + ", numberFormatter=" + numberFormatter
+                    + ", dateFormatter=" + dateFormatter
+                    + '}';
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/InheritanceInstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/deserializer/InheritanceInstanceCreator.java
new file mode 100644
index 0000000..efcc6c5
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/InheritanceInstanceCreator.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import jakarta.json.JsonObject;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.jsonstructure.JsonStructureToParserAdapter;
+import org.eclipse.yasson.internal.model.customization.TypeInheritanceConfiguration;
+
+import static jakarta.json.stream.JsonParser.Event;
+
+/**
+ * Instance creator following the inheritance structure defined by {@link jakarta.json.bind.annotation.JsonbTypeInfo}.
+ */
+class InheritanceInstanceCreator implements ModelDeserializer<JsonParser> {
+
+    private final Class<?> processedType;
+    private final Map<String, Class<?>> resolvedClasses = new ConcurrentHashMap<>();
+    private final DeserializationModelCreator deserializationModelCreator;
+    private final TypeInheritanceConfiguration typeInheritanceConfiguration;
+    private final ModelDeserializer<JsonParser> defaultProcessor;
+
+    InheritanceInstanceCreator(Class<?> processedType,
+                               DeserializationModelCreator deserializationModelCreator,
+                               TypeInheritanceConfiguration typeInheritanceConfiguration,
+                               ModelDeserializer<JsonParser> defaultProcessor) {
+        this.processedType = processedType;
+        this.deserializationModelCreator = deserializationModelCreator;
+        this.typeInheritanceConfiguration = typeInheritanceConfiguration;
+        this.defaultProcessor = defaultProcessor;
+    }
+
+    @Override
+    public Object deserialize(JsonParser parser, DeserializationContextImpl context) {
+        String alias;
+        JsonParser jsonParser;
+        String polymorphismKeyName = typeInheritanceConfiguration.getFieldName();
+        JsonObject object = parser.getObject();
+        alias = object.getString(polymorphismKeyName, null);
+        JsonObject newJsonObject = context.getJsonbContext().getJsonProvider().createObjectBuilder(object)
+                .remove(polymorphismKeyName)
+                .build();
+        jsonParser = new JsonStructureToParserAdapter(newJsonObject);
+        //To get to the first event
+        Event event = jsonParser.next();
+        context.setLastValueEvent(event);
+        Class<?> polymorphicTypeClass;
+        if (alias == null) {
+            return defaultProcessor.deserialize(jsonParser, context);
+        }
+        polymorphicTypeClass = getPolymorphicTypeClass(alias);
+        if (polymorphicTypeClass.equals(processedType)) {
+            return defaultProcessor.deserialize(jsonParser, context);
+        }
+        ModelDeserializer<JsonParser> deserializer = deserializationModelCreator.deserializerChain(polymorphicTypeClass);
+        return deserializer.deserialize(jsonParser, context);
+    }
+
+    @Override
+    public String toString() {
+        return "Property " + typeInheritanceConfiguration.getFieldName() + " polymorphic information handler";
+    }
+
+    private Class<?> getPolymorphicTypeClass(String alias) {
+        if (resolvedClasses.containsKey(alias)) {
+            return resolvedClasses.get(alias);
+        }
+        for (Map.Entry<Class<?>, String> entry : typeInheritanceConfiguration.getAliases().entrySet()) {
+            if (entry.getValue().equals(alias)) {
+                resolvedClasses.put(alias, entry.getKey());
+                return entry.getKey();
+            }
+        }
+        throw new JsonbException("Unknown alias \"" + alias + "\" of the type " + processedType.getName() + ". Known aliases: "
+                                         + typeInheritanceConfiguration.getAliases().values());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/JsonbCreatorDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/JsonbCreatorDeserializer.java
new file mode 100644
index 0000000..e3dab9a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/JsonbCreatorDeserializer.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.model.CreatorModel;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import static org.eclipse.yasson.internal.deserializer.ObjectDeserializer.NOOP;
+import static org.eclipse.yasson.internal.deserializer.ObjectDeserializer.VALUE_SKIPPERS;
+
+/**
+ * Creator of the Object instance with the usage of the {@link JsonbCreator}.
+ */
+class JsonbCreatorDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final Map<String, ModelDeserializer<JsonParser>> propertyDeserializerChains;
+    private final Map<String, ModelDeserializer<Object>> defaultCreatorValues;
+    private final List<String> creatorParams;
+    private final Set<String> ignoredProperties;
+    private final JsonbCreator creator;
+    private final Class<?> clazz;
+    private final Function<String, String> renamer;
+    private final boolean failOnUnknownProperties;
+
+    JsonbCreatorDeserializer(Map<String, ModelDeserializer<JsonParser>> propertyDeserializerChains,
+                             Map<String, ModelDeserializer<Object>> defaultCreatorValues,
+                             JsonbCreator creator,
+                             Class<?> clazz,
+                             Function<String, String> renamer,
+                             boolean failOnUnknownProperties,
+                             Set<String> ignoredProperties) {
+        this.propertyDeserializerChains = propertyDeserializerChains;
+        this.defaultCreatorValues = defaultCreatorValues;
+        this.creatorParams = Arrays.stream(creator.getParams()).map(CreatorModel::getName).collect(Collectors.toList());
+        this.ignoredProperties = Set.copyOf(ignoredProperties);
+        this.creator = creator;
+        this.clazz = clazz;
+        this.renamer = renamer;
+        this.failOnUnknownProperties = failOnUnknownProperties;
+    }
+
+    @Override
+    public Object deserialize(JsonParser parser, DeserializationContextImpl context) {
+        String key = null;
+        Map<String, Object> paramValues = new HashMap<>();
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            context.setLastValueEvent(next);
+            switch (next) {
+            case KEY_NAME:
+                key = renamer.apply(parser.getString());
+                break;
+            case VALUE_NULL:
+            case START_OBJECT:
+            case START_ARRAY:
+            case VALUE_STRING:
+            case VALUE_NUMBER:
+            case VALUE_FALSE:
+            case VALUE_TRUE:
+                if (propertyDeserializerChains.containsKey(key)) {
+                    try {
+                        Object o = propertyDeserializerChains.get(key).deserialize(parser, context);
+                        if (creatorParams.contains(key)) {
+                            paramValues.put(key, o);
+                        }
+                    } catch (JsonbException e) {
+                        throw new JsonbException("Unable to deserialize property '" + key + "' because of: " + e.getMessage(), e);
+                    }
+                } else if (failOnUnknownProperties && !ignoredProperties.contains(key)) {
+                    throw new JsonbException(Messages.getMessage(MessageKeys.UNKNOWN_JSON_PROPERTY, key, clazz));
+                } else {
+                    //We need to skip the corresponding structure if property key was not found
+                    VALUE_SKIPPERS.getOrDefault(next, NOOP).accept(parser);
+                }
+                break;
+            case END_OBJECT:
+                Object[] params = new Object[creatorParams.size()];
+                for (int i = 0; i < creatorParams.size(); i++) {
+                    String param = creatorParams.get(i);
+                    if (paramValues.containsKey(param)) {
+                        params[i] = paramValues.get(param);
+                    } else {
+                        params[i] = defaultCreatorValues.get(param).deserialize(null, context);
+                    }
+                }
+                context.setInstance(creator.call(params, clazz));
+                context.getDeferredDeserializers().forEach(Runnable::run);
+                context.getDeferredDeserializers().clear();
+                return context.getInstance();
+            default:
+                throw new JsonbException("Unexpected state: " + next);
+            }
+        }
+        return context.getInstance();
+    }
+
+    @Override
+    public String toString() {
+        return "ObjectInstanceCreator{"
+                + "parameters=" + creatorParams
+                + ", clazz=" + clazz
+                + '}';
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/JustReturn.java b/src/main/java/org/eclipse/yasson/internal/deserializer/JustReturn.java
new file mode 100644
index 0000000..6fcc10c
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/JustReturn.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Return passed in object value.
+ */
+public final class JustReturn implements ModelDeserializer<Object> {
+
+    private static final JustReturn INSTANCE = new JustReturn();
+
+    private JustReturn() {
+    }
+
+    /**
+     * Return instance.
+     *
+     * @return instance of the class
+     */
+    public static JustReturn instance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Object deserialize(Object value, DeserializationContextImpl context) {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return "No other operations will be performed";
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/MapDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/MapDeserializer.java
new file mode 100644
index 0000000..d366fc8
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/MapDeserializer.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.util.Map;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Map container deserializer.
+ */
+class MapDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> keyDelegate;
+    private final ModelDeserializer<JsonParser> valueDelegate;
+
+    MapDeserializer(ModelDeserializer<JsonParser> keyDelegate,
+                    ModelDeserializer<JsonParser> valueDelegate) {
+        this.keyDelegate = keyDelegate;
+        this.valueDelegate = valueDelegate;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object deserialize(JsonParser parser, DeserializationContextImpl context) {
+        Map<Object, Object> map = (Map<Object, Object>) context.getInstance();
+        Object key = null;
+        Object keyValue = null;
+        String keyName = null;
+        Mode mode = Mode.NONE;
+        State state = State.NEXT;
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            context.setLastValueEvent(next);
+            switch (next) {
+            case KEY_NAME:
+                mode = mode == Mode.NONE ? Mode.NORMAL : mode;
+                if (mode == Mode.NORMAL) {
+                    keyValue = deserializeValue(parser, context, keyDelegate);
+                }
+                keyName = parser.getString();
+                break;
+            case START_OBJECT:
+                mode = mode == Mode.NONE ? Mode.OBJECT : mode;
+            case START_ARRAY:
+            case VALUE_STRING:
+            case VALUE_TRUE:
+            case VALUE_FALSE:
+            case VALUE_NUMBER:
+            case VALUE_NULL:
+                if (mode == Mode.OBJECT) {
+                    if (state == State.NEXT) {
+                        state = State.KEY;
+                    } else if (state == State.KEY) {
+                        validateKeyName(keyName, state);
+                        key = deserializeValue(parser, context, keyDelegate);
+                        state = State.VALUE;
+                    } else if (state == State.VALUE) {
+                        validateKeyName(keyName, state);
+                        Object value = deserializeValue(parser, context, valueDelegate);
+                        map.put(key, value);
+                        state = State.DONE;
+                    } else {
+                        throw new JsonbException("Only attributes 'key' and 'value' allowed!");
+                    }
+                } else {
+                    Object value = deserializeValue(parser, context, valueDelegate);
+                    map.put(keyValue, value);
+                }
+                break;
+            case END_OBJECT:
+                state = State.NEXT;
+                if (mode == Mode.OBJECT) {
+                    break;
+                }
+            case END_ARRAY:
+                return map;
+            default:
+                throw new JsonbException("Unexpected state: " + next);
+            }
+        }
+        return map;
+    }
+
+    private void validateKeyName(String keyName, State state) {
+        if (state == State.KEY && !keyName.equals("key")) {
+            throw new JsonbException("Attribute name has to be 'key' when representing map entry key. Got: " + keyName);
+        } else if (state == State.VALUE && !keyName.equals("value")) {
+            throw new JsonbException("Attribute name has to be 'value' when representing map entry value. Got: " + keyName);
+        }
+    }
+
+    private Object deserializeValue(JsonParser parser,
+                                    DeserializationContextImpl context,
+                                    ModelDeserializer<JsonParser> deserializer) {
+        DeserializationContextImpl keyContext = new DeserializationContextImpl(context);
+        return deserializer.deserialize(parser, keyContext);
+    }
+
+    private enum Mode {
+
+        NONE,
+        NORMAL,
+        OBJECT
+
+    }
+
+    private enum State {
+
+        NEXT,
+        VALUE,
+        KEY,
+        DONE
+
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/MapInstanceCreator.java b/src/main/java/org/eclipse/yasson/internal/deserializer/MapInstanceCreator.java
new file mode 100644
index 0000000..580c17c
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/MapInstanceCreator.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.InstanceCreator;
+import org.eclipse.yasson.internal.JsonbConfigProperties;
+
+/**
+ * Map instance creator.
+ */
+class MapInstanceCreator implements ModelDeserializer<JsonParser> {
+
+    private final MapDeserializer delegate;
+    private final JsonbConfigProperties configProperties;
+    private final Class<?> clazz;
+
+    MapInstanceCreator(MapDeserializer delegate,
+                       JsonbConfigProperties configProperties,
+                       Class<?> clazz) {
+        this.delegate = delegate;
+        this.configProperties = configProperties;
+        this.clazz = clazz;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        Map<?, ?> map = createInstance(clazz);
+        context.setInstance(map);
+        return delegate.deserialize(value, context);
+    }
+
+    private Map<?, ?> createInstance(Class<?> clazz) {
+        return clazz.isInterface()
+                ? getMapImpl(clazz)
+                : (Map<?, ?>) InstanceCreator.createInstance(clazz);
+    }
+
+    private Map<?, ?> getMapImpl(Class<?> ifcType) {
+        if (ConcurrentMap.class.isAssignableFrom(ifcType)) {
+            if (SortedMap.class.isAssignableFrom(ifcType) || NavigableMap.class.isAssignableFrom(ifcType)) {
+                return new ConcurrentSkipListMap<>();
+            } else {
+                return new ConcurrentHashMap<>();
+            }
+        }
+        // SortedMap, NavigableMap
+        if (SortedMap.class.isAssignableFrom(ifcType)) {
+            Class<?> defaultMapImplType = configProperties.getDefaultMapImplType();
+            return SortedMap.class.isAssignableFrom(defaultMapImplType)
+                    ? (Map<?, ?>) InstanceCreator.createInstance(defaultMapImplType)
+                    : new TreeMap<>();
+        }
+        return new HashMap<>();
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/ModelDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/ModelDeserializer.java
new file mode 100644
index 0000000..2275e3c
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/ModelDeserializer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Type deserializer.
+ * <br>
+ * All the instances are required to be reusable and without any states
+ * stored in the class fields.
+ *
+ * @param <T> represents the content value this deserializer is using
+ */
+public interface ModelDeserializer<T> {
+
+    /**
+     * Deserialize provided value or delegate deserialization to the next deserializer.
+     *
+     * @param value   value to be deserialized
+     * @param context deserialization context
+     * @return deserialized value
+     */
+    Object deserialize(T value, DeserializationContextImpl context);
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/NullCheckDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/NullCheckDeserializer.java
new file mode 100644
index 0000000..c6310ce
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/NullCheckDeserializer.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Json null value checker.
+ * <br>
+ * Simple delegate which checks whether the obtained parser value event was
+ * {@link JsonParser.Event#VALUE_NULL} or not. If the event has been {@link JsonParser.Event#VALUE_NULL}, null value
+ * deserializer will be called. In all other cases non-null deserializer is called.
+ */
+public class NullCheckDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> nonNullDeserializer;
+    private final ModelDeserializer<Object> nullDeserializer;
+
+    /**
+     * Create new instance.
+     *
+     * @param nonNullDeserializer deserializer called when value is not null
+     * @param nullDeserializer    deserializer called when value is null
+     */
+    public NullCheckDeserializer(ModelDeserializer<JsonParser> nonNullDeserializer,
+                                 ModelDeserializer<Object> nullDeserializer) {
+        this.nonNullDeserializer = nonNullDeserializer;
+        this.nullDeserializer = nullDeserializer;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        if (context.getLastValueEvent() != JsonParser.Event.VALUE_NULL) {
+            return nonNullDeserializer.deserialize(value, context);
+        }
+        return nullDeserializer.deserialize(null, context);
+    }
+
+    @Override
+    public String toString() {
+        return "Null value check";
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/ObjectDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/ObjectDeserializer.java
new file mode 100644
index 0000000..fa7d914
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/ObjectDeserializer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2021, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Object container deserializer.
+ */
+class ObjectDeserializer implements ModelDeserializer<JsonParser> {
+
+    static final Consumer<JsonParser> NOOP = jsonParser -> {};
+    static final EnumMap<JsonParser.Event, Consumer<JsonParser>> VALUE_SKIPPERS = new EnumMap<>(JsonParser.Event.class);
+
+    static {
+        VALUE_SKIPPERS.put(JsonParser.Event.START_OBJECT, JsonParser::skipObject);
+        VALUE_SKIPPERS.put(JsonParser.Event.START_ARRAY, JsonParser::skipArray);
+    }
+
+    private final Map<String, ModelDeserializer<JsonParser>> propertyDeserializerChains;
+    private final Function<String, String> renamer;
+    private final Class<?> rawClass;
+    private final boolean failOnUnknownProperty;
+    private final Set<String> ignoredProperties;
+
+    ObjectDeserializer(Map<String, ModelDeserializer<JsonParser>> propertyDeserializerChains,
+                       Function<String, String> renamer,
+                       Class<?> rawClass,
+                       boolean failOnUnknownProperty,
+                       Set<String> ignoredProperties) {
+        this.propertyDeserializerChains = Map.copyOf(propertyDeserializerChains);
+        this.renamer = renamer;
+        this.rawClass = rawClass;
+        this.failOnUnknownProperty = failOnUnknownProperty;
+        this.ignoredProperties = Set.copyOf(ignoredProperties);
+    }
+
+    @Override
+    public Object deserialize(JsonParser parser, DeserializationContextImpl context) {
+        String key = null;
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            context.setLastValueEvent(next);
+            switch (next) {
+            case KEY_NAME:
+                key = renamer.apply(parser.getString());
+                break;
+            case VALUE_NULL:
+            case START_OBJECT:
+            case START_ARRAY:
+            case VALUE_STRING:
+            case VALUE_NUMBER:
+            case VALUE_FALSE:
+            case VALUE_TRUE:
+                if (propertyDeserializerChains.containsKey(key)) {
+                    try {
+                        propertyDeserializerChains.get(key).deserialize(parser, context);
+                    } catch (JsonbException e) {
+                        throw new JsonbException("Unable to deserialize property '" + key + "' because of: " + e.getMessage(), e);
+                    }
+                } else if (failOnUnknownProperty && !ignoredProperties.contains(key)) {
+                    throw new JsonbException(Messages.getMessage(MessageKeys.UNKNOWN_JSON_PROPERTY, key, rawClass));
+                } else {
+                    //We need to skip the corresponding structure if property key was not found
+                    VALUE_SKIPPERS.getOrDefault(next, NOOP).accept(parser);
+                }
+                break;
+            case END_ARRAY:
+                break;
+            case END_OBJECT:
+                return context.getInstance();
+            default:
+                throw new JsonbException("Unexpected state: " + next);
+            }
+        }
+        return context.getInstance();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/OptionalDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/OptionalDeserializer.java
new file mode 100644
index 0000000..2bc71ab
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/OptionalDeserializer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.util.Optional;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Optional} types.
+ */
+class OptionalDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> typeDeserializer;
+    private final ModelDeserializer<Object> delegate;
+
+    OptionalDeserializer(ModelDeserializer<JsonParser> typeDeserializer,
+                         ModelDeserializer<Object> delegate) {
+        this.typeDeserializer = typeDeserializer;
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        Optional<Object> val = Optional.ofNullable(typeDeserializer.deserialize(value, context));
+        return delegate.deserialize(val, context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/PositionChecker.java b/src/main/java/org/eclipse/yasson/internal/deserializer/PositionChecker.java
new file mode 100644
index 0000000..61e4cb5
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/PositionChecker.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+import static jakarta.json.stream.JsonParser.Event;
+
+/**
+ * JSON document position checker.
+ * <br>
+ * Checks whether json parser is in expected state. If not it will try to skip to the next event, since
+ * if user defined components are involved, it is possible to expect incorrect states in terms of the last expected events.
+ * If this checker is still not in expected state, an exception is thrown.
+ */
+public class PositionChecker implements ModelDeserializer<JsonParser> {
+
+    private static final Map<Event, Event> CLOSING_EVENTS = Map.of(Event.START_ARRAY, Event.END_ARRAY,
+                                                                   Event.START_OBJECT, Event.END_OBJECT);
+
+    private final Set<Event> expectedEvents;
+    private final ModelDeserializer<JsonParser> delegate;
+    private final Type rType;
+
+    /**
+     * Create new instance.
+     *
+     * @param delegate delegate which is call after the check
+     * @param rType    runtime type
+     * @param checker  bound group of events
+     */
+    public PositionChecker(ModelDeserializer<JsonParser> delegate, Type rType, Checker checker) {
+        this(checker.events, delegate, rType);
+    }
+
+    /**
+     * Create new instance.
+     *
+     * @param delegate delegate which is call after the check
+     * @param rType    runtime type
+     * @param events   customized checked events
+     */
+    public PositionChecker(ModelDeserializer<JsonParser> delegate, Type rType, Event... events) {
+        this(Set.copyOf(Arrays.asList(events)), delegate, rType);
+    }
+
+    private PositionChecker(Set<Event> expectedEvents,
+                            ModelDeserializer<JsonParser> delegate, Type rType) {
+        this.expectedEvents = expectedEvents;
+        this.delegate = delegate;
+        this.rType = rType;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        Event original = context.getLastValueEvent();
+        Event startEvent = original;
+        if (!expectedEvents.contains(startEvent)) {
+            startEvent = value.next();
+            context.setLastValueEvent(startEvent);
+            if (!expectedEvents.contains(startEvent)) {
+                throw new JsonbException("Incorrect position for processing type: " + rType + ". "
+                                                 + "Received event: " + original + " "
+                                                 + "Allowed: " + expectedEvents);
+            }
+        }
+        Object o = delegate.deserialize(value, context);
+        if (CLOSING_EVENTS.containsKey(startEvent)
+                && CLOSING_EVENTS.get(startEvent) != context.getLastValueEvent()) {
+            throw new JsonbException("Incorrect parser position after processing of the type: " + rType + ". "
+                                             + "Start event: " + startEvent + " "
+                                             + "After processing event: " + context.getLastValueEvent());
+        }
+        return o;
+    }
+
+    @Override
+    public String toString() {
+        return "PositionChecker{"
+                + "expectedEvents=" + expectedEvents
+                + ", runtimeType=" + rType
+                + '}';
+    }
+
+    /**
+     * Grouped events according to whether it is container or value.
+     */
+    public enum Checker {
+
+        /**
+         * Value bound events.
+         */
+        VALUES(Event.VALUE_FALSE,
+               Event.VALUE_TRUE,
+               Event.VALUE_STRING,
+               Event.VALUE_NUMBER,
+               Event.VALUE_NULL),
+
+        /**
+         * Container bound events.
+         */
+        CONTAINER(Event.START_OBJECT,
+                  Event.START_ARRAY);
+
+        private final Set<Event> events;
+
+        Checker(Event... events) {
+            this.events = Set.of(events);
+        }
+
+        /**
+         * Return events bound to the event group.
+         *
+         * @return set of bound events
+         */
+        public Set<Event> getEvents() {
+            return events;
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/RequiredCreatorParameter.java b/src/main/java/org/eclipse/yasson/internal/deserializer/RequiredCreatorParameter.java
new file mode 100644
index 0000000..021817b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/RequiredCreatorParameter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+class RequiredCreatorParameter implements ModelDeserializer<Object> {
+
+    private final String parameterName;
+
+    RequiredCreatorParameter(String parameterName) {
+        this.parameterName = parameterName;
+    }
+
+    @Override
+    public Object deserialize(Object value, DeserializationContextImpl context) {
+        throw new JsonbException(Messages.getMessage(MessageKeys.JSONB_CREATOR_MISSING_PROPERTY, parameterName));
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/UserDefinedDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/UserDefinedDeserializer.java
new file mode 100644
index 0000000..70ec440
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/UserDefinedDeserializer.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.model.customization.Customization;
+
+/**
+ * Deserializer used to invoke user defined deserializers.
+ */
+class UserDefinedDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final JsonbDeserializer<?> userDefinedDeserializer;
+    private final ModelDeserializer<Object> delegate;
+    private final Type rType;
+    private final Customization customization;
+
+    //TODO remove or not? deserializer cycle
+    //    public UserDefinedDeserializer(JsonbDeserializer<?> userDefinedDeserializer,
+    //                                   ModelDeserializer<JsonParser> exactType,
+    //                                   ModelDeserializer<Object> delegate,
+    //                                   Type rType,
+    //                                   Customization customization) {
+    //        this.userDefinedDeserializer = userDefinedDeserializer;
+    //        this.exactType = exactType;
+    //        this.delegate = delegate;
+    //        this.rType = rType;
+    //        this.customization = customization;
+    //    }
+    UserDefinedDeserializer(JsonbDeserializer<?> userDefinedDeserializer,
+                            ModelDeserializer<Object> delegate,
+                            Type rType,
+                            Customization customization) {
+        this.userDefinedDeserializer = userDefinedDeserializer;
+        this.delegate = delegate;
+        this.rType = rType;
+        this.customization = customization;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        DeserializationContextImpl newContext = new DeserializationContextImpl(context);
+        newContext.setCustomization(customization);
+        //TODO remove or not? deserializer cycle
+        //        if (context.getUserProcessorChain().contains(userDefinedDeserializer.getClass())) {
+        //            if (context.getLastValueEvent() != JsonParser.Event.START_ARRAY
+        //                    && context.getLastValueEvent() != JsonParser.Event.START_OBJECT) {
+        //                newContext.setDisableNextPositionCheck(true);
+        //            }
+        //            return exactType.deserialize(value, newContext);
+        //        }
+        //        newContext.getUserProcessorChain().add(userDefinedDeserializer.getClass());
+        YassonParser yassonParser = new YassonParser(value, context.getLastValueEvent(), newContext);
+        Object object = userDefinedDeserializer.deserialize(yassonParser, newContext, rType);
+        yassonParser.skipRemaining();
+        context.setLastValueEvent(newContext.getLastValueEvent());
+        return delegate.deserialize(object, context);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/ValueExtractor.java b/src/main/java/org/eclipse/yasson/internal/deserializer/ValueExtractor.java
new file mode 100644
index 0000000..f55c049
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/ValueExtractor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.deserializer.types.TypeDeserializer;
+
+/**
+ * Extracts the value out of the {@link JsonParser} based upon the last obtained event.
+ */
+public class ValueExtractor implements ModelDeserializer<JsonParser> {
+
+    private final TypeDeserializer delegate;
+
+    /**
+     * Create new instance.
+     *
+     * @param delegate delegate to accept extracted value
+     */
+    public ValueExtractor(TypeDeserializer delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        JsonParser.Event last = context.getLastValueEvent();
+        switch (last) {
+        case VALUE_TRUE:
+            return delegate.deserialize(Boolean.TRUE, context);
+        case VALUE_FALSE:
+            return delegate.deserialize(Boolean.FALSE, context);
+        case KEY_NAME:
+        case VALUE_STRING:
+            return delegate.deserialize(value.getString(), context);
+        case VALUE_NUMBER:
+            //We don't know for sure how to handle the number value, it can be int, long etc.
+            //Value extraction has to be delegated to the TypeDeserializer
+            return delegate.deserialize(value, context);
+        default:
+            throw new JsonbException("Could not extract data. Received event: " + last);
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/ValueSetterDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/ValueSetterDeserializer.java
new file mode 100644
index 0000000..857e55a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/ValueSetterDeserializer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.lang.invoke.MethodHandle;
+import java.util.Objects;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Value setter. Invokes created {@link MethodHandle} to set deserialized value to the instance.
+ */
+class ValueSetterDeserializer implements ModelDeserializer<Object> {
+
+    private final MethodHandle valueSetter;
+
+    ValueSetterDeserializer(MethodHandle valueSetter) {
+        this.valueSetter = Objects.requireNonNull(valueSetter);
+    }
+
+    @Override
+    public Object deserialize(Object value, DeserializationContextImpl context) {
+        Object object = context.getInstance();
+        try {
+            valueSetter.invoke(object, value);
+            return value;
+        } catch (Throwable e) {
+            throw new JsonbException("Error setting value on: " + object, e);
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/YassonParser.java b/src/main/java/org/eclipse/yasson/internal/deserializer/YassonParser.java
new file mode 100644
index 0000000..c17f6ff
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/YassonParser.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer;
+
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.stream.Stream;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Yasson {@link YassonParser} parser wrapper.
+ * <br>
+ * Used for user defined deserializers. Does not allow deserializer to read outside the scope it should be used on.
+ */
+class YassonParser implements JsonParser {
+
+    private final JsonParser delegate;
+    private final DeserializationContextImpl context;
+    private int level;
+
+    YassonParser(JsonParser delegate, Event firstEvent, DeserializationContextImpl context) {
+        this.delegate = delegate;
+        this.context = context;
+        this.level = determineLevelValue(firstEvent);
+    }
+
+    private int determineLevelValue(Event firstEvent) {
+        switch (firstEvent) {
+        case START_ARRAY:
+        case START_OBJECT:
+            return 1; //container start, there will be more events to come
+        default:
+            return 0; //just this single value, do not allow reading more
+        }
+    }
+
+    void skipRemaining() {
+        while (hasNext()) {
+            next();
+        }
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (level < 1) {
+            return false;
+        }
+        return delegate.hasNext();
+    }
+
+    @Override
+    public Event next() {
+        validate();
+        Event next = delegate.next();
+        context.setLastValueEvent(next);
+        switch (next) {
+        case START_OBJECT:
+        case START_ARRAY:
+            level++;
+            break;
+        case END_OBJECT:
+        case END_ARRAY:
+            level--;
+            break;
+        default:
+            //no other changes needed
+        }
+        return next;
+    }
+
+    @Override
+    public String getString() {
+        return delegate.getString();
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        return delegate.isIntegralNumber();
+    }
+
+    @Override
+    public int getInt() {
+        return delegate.getInt();
+    }
+
+    @Override
+    public long getLong() {
+        return delegate.getLong();
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        return delegate.getBigDecimal();
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        return delegate.getLocation();
+    }
+
+    @Override
+    public JsonObject getObject() {
+        validate();
+        level--;
+        JsonObject jsonObject = delegate.getObject();
+        context.setLastValueEvent(Event.END_OBJECT);
+        return jsonObject;
+    }
+
+    @Override
+    public JsonValue getValue() {
+        final Event currentLevel = context.getLastValueEvent();
+        switch (currentLevel) {
+        case START_ARRAY:
+            return getArray();
+        case START_OBJECT:
+            return getObject();
+        default:
+            return delegate.getValue();
+        }
+    }
+
+    @Override
+    public JsonArray getArray() {
+        validate();
+        level--;
+        JsonArray array = delegate.getArray();
+        context.setLastValueEvent(Event.END_ARRAY);
+        return array;
+    }
+
+    @Override
+    public Stream<JsonValue> getArrayStream() {
+        validate();
+        level--;
+        return delegate.getArrayStream();
+    }
+
+    @Override
+    public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+        validate();
+        level--;
+        return delegate.getObjectStream();
+    }
+
+    @Override
+    public Stream<JsonValue> getValueStream() {
+        validate();
+        level--;
+        return delegate.getValueStream();
+    }
+
+    @Override
+    public void skipArray() {
+        validate();
+        level--;
+        delegate.skipArray();
+    }
+
+    @Override
+    public void skipObject() {
+        validate();
+        level--;
+        delegate.skipObject();
+    }
+
+    @Override
+    public void close() {
+        throw new UnsupportedOperationException();
+    }
+
+    private void validate() {
+        if (level < 1) {
+            throw new NoSuchElementException("There are no more elements available!");
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractDateDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractDateDeserializer.java
new file mode 100644
index 0000000..865a243
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractDateDeserializer.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.sql.Date;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+import java.util.Optional;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.annotation.JsonbDateFormat;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.JsonbConfigProperties;
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.deserializer.JustReturn;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+import org.eclipse.yasson.internal.model.customization.Customization;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Base deserializer for all the date related types.
+ */
+abstract class AbstractDateDeserializer<T> extends TypeDeserializer {
+
+    static final ZoneId UTC = ZoneId.of("UTC");
+
+    private ModelDeserializer<String> actualDeserializer;
+
+    AbstractDateDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+        this.actualDeserializer = actualDeserializer(builder.getConfigProperties(), builder.getCustomization());
+    }
+
+    AbstractDateDeserializer(Class<Date> clazz) {
+        super(new TypeDeserializerBuilder(clazz, null, null, JustReturn.instance()));
+        this.actualDeserializer = null;
+    }
+
+    private ModelDeserializer<String> actualDeserializer(JsonbConfigProperties properties, Customization customization) {
+        final JsonbDateFormatter formatter = getJsonbDateFormatter(properties, customization);
+        if (JsonbDateFormat.TIME_IN_MILLIS.equals(formatter.getFormat())) {
+            return (value, context) -> fromInstant(Instant.ofEpochMilli(Long.parseLong(value)));
+        } else if (formatter.getDateTimeFormatter() != null) {
+            return (value, context) -> parseWithFormatterInternal(value, formatter.getDateTimeFormatter());
+        } else {
+            DateTimeFormatter configDateTimeFormatter = properties.getConfigDateFormatter().getDateTimeFormatter();
+            if (configDateTimeFormatter != null) {
+                return (value, context) -> parseWithFormatterInternal(value, configDateTimeFormatter);
+            }
+        }
+        if (properties.isStrictIJson()) {
+            return (value, context) -> parseWithFormatterInternal(value, JsonbDateFormatter.IJSON_DATE_FORMATTER);
+        }
+        Locale locale = properties.getLocale(formatter.getLocale());
+        return (value, context) -> {
+            try {
+                return parseDefault(value, locale);
+            } catch (DateTimeException e) {
+                throw new JsonbException(Messages.getMessage(MessageKeys.DATE_PARSE_ERROR, value, getType()), e);
+            }
+        };
+    }
+
+    private JsonbDateFormatter getJsonbDateFormatter(JsonbConfigProperties properties, Customization customization) {
+        return Optional.ofNullable(customization.getDeserializeDateFormatter())
+                .orElse(properties.getConfigDateFormatter());
+    }
+
+    @Override
+    public Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        if (actualDeserializer == null) {
+            actualDeserializer = actualDeserializer(context.getJsonbContext().getConfigProperties(), context.getCustomization());
+        }
+        return actualDeserializer.deserialize(value, context);
+    }
+
+    /**
+     * Construct date object from an instant containing epoch millisecond.
+     * If date object supports zone offset / zone id, system default is used and warning is logged.
+     *
+     * @param instant instant to construct from
+     * @return date object
+     */
+    abstract T fromInstant(Instant instant);
+
+    /**
+     * Parse java.time date object with default formatter.
+     * Different default formatter for each date object type is used.
+     *
+     * @param jsonValue string value to parse from
+     * @param locale    annotated locale or default
+     * @return parsed date object
+     */
+    abstract T parseDefault(String jsonValue, Locale locale);
+
+    /**
+     * Parse java.time date object with provided formatter.
+     *
+     * @param jsonValue string value to parse from
+     * @param formatter a formatter to use
+     * @return parsed date object
+     */
+    abstract T parseWithFormatter(String jsonValue, DateTimeFormatter formatter);
+
+    private T parseWithFormatterInternal(String jsonValue, DateTimeFormatter formatter) {
+        try {
+            return parseWithFormatter(jsonValue, formatter);
+        } catch (DateTimeException e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.DATE_PARSE_ERROR, jsonValue, getType()), e);
+        }
+    }
+
+    protected DateTimeFormatter getZonedFormatter(DateTimeFormatter formatter) {
+        return formatter.getZone() != null
+                ? formatter
+                : formatter.withZone(UTC);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractNumberDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractNumberDeserializer.java
new file mode 100644
index 0000000..e50e003
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/AbstractNumberDeserializer.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+import java.util.function.Function;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+import org.eclipse.yasson.internal.model.customization.Customization;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Base deserializer for all the number types.
+ */
+abstract class AbstractNumberDeserializer<T extends Number> extends TypeDeserializer {
+
+    private final ModelDeserializer<String> actualDeserializer;
+    private final boolean integerOnly;
+
+    AbstractNumberDeserializer(TypeDeserializerBuilder builder, boolean integerOnly) {
+        super(builder);
+        this.actualDeserializer = actualDeserializer(builder);
+        this.integerOnly = integerOnly;
+    }
+
+    private ModelDeserializer<String> actualDeserializer(TypeDeserializerBuilder builder) {
+        Customization customization = builder.getCustomization();
+        if (customization.getDeserializeNumberFormatter() == null) {
+            return (value, context) -> {
+                try {
+                    return parseNumberValue(value);
+                } catch (NumberFormatException e) {
+                    throw new JsonbException(Messages.getMessage(MessageKeys.DESERIALIZE_VALUE_ERROR, getType()), e);
+                }
+            };
+        }
+
+        final JsonbNumberFormatter numberFormat = customization.getDeserializeNumberFormatter();
+        //consider synchronizing on format instance or per thread cache.
+        Locale locale = builder.getConfigProperties().getLocale(numberFormat.getLocale());
+        final NumberFormat format = NumberFormat.getInstance(locale);
+        ((DecimalFormat) format).applyPattern(numberFormat.getFormat());
+        format.setParseIntegerOnly(integerOnly);
+        Function<String, String> valueChanger = createCompatibilityValueChanger(locale);
+        return (value, context) -> {
+            try {
+                String updated = valueChanger.apply(value);
+                return parseNumberValue(String.valueOf(format.parse(updated)));
+            } catch (ParseException e) {
+                throw new JsonbException(Messages.getMessage(MessageKeys.PARSING_NUMBER, value, numberFormat.getFormat()), e);
+            }
+        };
+    }
+
+    private Function<String, String> createCompatibilityValueChanger(Locale locale) {
+        char beforeJdk13GroupSeparator = '\u00A0';
+        char frenchGroupingSeparator = DecimalFormatSymbols.getInstance(Locale.FRENCH).getGroupingSeparator();
+        if (locale.getLanguage().equals(Locale.FRENCH.getLanguage()) && beforeJdk13GroupSeparator != frenchGroupingSeparator) {
+            //JDK-8225245
+            return value -> value.replace(beforeJdk13GroupSeparator, frenchGroupingSeparator);
+        }
+        return value -> value;
+    }
+
+    abstract T parseNumberValue(String value);
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return actualDeserializer.deserialize(value, context);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/BigDecimalDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/BigDecimalDeserializer.java
new file mode 100644
index 0000000..7eda5b7
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/BigDecimalDeserializer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.math.BigDecimal;
+
+/**
+ * Deserializer of the {@link BigDecimal} type.
+ */
+class BigDecimalDeserializer extends AbstractNumberDeserializer<BigDecimal> {
+
+    BigDecimalDeserializer(TypeDeserializerBuilder builder) {
+        super(builder, false);
+    }
+
+    @Override
+    BigDecimal parseNumberValue(String value) {
+        return new BigDecimal(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/BigIntegerDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/BigIntegerDeserializer.java
new file mode 100644
index 0000000..92670bb
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/BigIntegerDeserializer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.math.BigInteger;
+
+/**
+ * Deserializer of the {@link BigInteger} type.
+ */
+class BigIntegerDeserializer extends AbstractNumberDeserializer<BigInteger> {
+
+    BigIntegerDeserializer(TypeDeserializerBuilder builder) {
+        super(builder, true);
+    }
+
+    @Override
+    BigInteger parseNumberValue(String value) {
+        return new BigInteger(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/BooleanDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/BooleanDeserializer.java
new file mode 100644
index 0000000..ddbf0bb
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/BooleanDeserializer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Boolean} type.
+ */
+class BooleanDeserializer extends TypeDeserializer {
+
+    BooleanDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    public Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return Boolean.parseBoolean(value);
+    }
+
+    @Override
+    Object deserializeBooleanValue(boolean value, DeserializationContextImpl context, Type rType) {
+        return value;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/ByteDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ByteDeserializer.java
new file mode 100644
index 0000000..21762ed
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ByteDeserializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+/**
+ * Deserializer of the {@link Byte} type.
+ */
+class ByteDeserializer extends AbstractNumberDeserializer<Byte> {
+
+    ByteDeserializer(TypeDeserializerBuilder builder) {
+        super(builder, true);
+    }
+
+    @Override
+    Byte parseNumberValue(String value) {
+        return Byte.parseByte(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/CalendarDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/CalendarDeserializer.java
new file mode 100644
index 0000000..b933d81
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/CalendarDeserializer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalQueries;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Deserializer of the {@link Calendar} type.
+ */
+class CalendarDeserializer extends AbstractDateDeserializer<Calendar> {
+
+    private static final LocalTime ZERO_LOCAL_TIME = LocalTime.parse("00:00:00");
+
+    private final Calendar calendarTemplate;
+
+    CalendarDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+        this.calendarTemplate = new GregorianCalendar();
+        this.calendarTemplate.clear();
+        this.calendarTemplate.setTimeZone(TimeZone.getTimeZone(UTC));
+    }
+
+    @Override
+    Calendar fromInstant(Instant instant) {
+        final Calendar calendar = (Calendar) calendarTemplate.clone();
+        calendar.setTimeInMillis(instant.toEpochMilli());
+        return calendar;
+    }
+
+    @Override
+    Calendar parseDefault(String jsonValue, Locale locale) {
+        DateTimeFormatter formatter = jsonValue.contains("T")
+                ? DateTimeFormatter.ISO_DATE_TIME
+                : DateTimeFormatter.ISO_DATE;
+        return parseWithFormatter(jsonValue, formatter.withLocale(locale));
+    }
+
+    @Override
+    Calendar parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        final TemporalAccessor parsed = formatter.parse(jsonValue);
+        LocalTime time = parsed.query(TemporalQueries.localTime());
+        ZoneId zone = parsed.query(TemporalQueries.zone());
+        if (zone == null) {
+            zone = UTC;
+        }
+        if (time == null) {
+            time = ZERO_LOCAL_TIME;
+        }
+        ZonedDateTime result = LocalDate.from(parsed).atTime(time).atZone(zone);
+        return GregorianCalendar.from(result);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/CharDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/CharDeserializer.java
new file mode 100644
index 0000000..aeb1b6a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/CharDeserializer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Character} type.
+ */
+class CharDeserializer extends TypeDeserializer {
+
+    CharDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return value.charAt(0);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/DateDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/DateDeserializer.java
new file mode 100644
index 0000000..723cdbe
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/DateDeserializer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Deserializer of the {@link Date} type.
+ */
+class DateDeserializer extends AbstractDateDeserializer<Date> {
+
+    private static final DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER = DateTimeFormatter.ISO_DATE_TIME;
+
+    DateDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Date fromInstant(Instant instant) {
+        return new Date(instant.toEpochMilli());
+    }
+
+    @Override
+    Date parseDefault(String jsonValue, Locale locale) {
+        return parseWithOrWithoutZone(jsonValue, DEFAULT_DATE_TIME_FORMATTER.withLocale(locale));
+    }
+
+    @Override
+    Date parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return parseWithOrWithoutZone(jsonValue, formatter);
+    }
+
+    private static Date parseWithOrWithoutZone(String jsonValue, DateTimeFormatter formatter) {
+        ZonedDateTime parsed;
+        if (formatter.getZone() == null) {
+            parsed = ZonedDateTime.parse(jsonValue, formatter.withZone(UTC));
+        } else {
+            parsed = ZonedDateTime.parse(jsonValue, formatter);
+        }
+        return Date.from(parsed.toInstant());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/DoubleDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/DoubleDeserializer.java
new file mode 100644
index 0000000..f64fe59
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/DoubleDeserializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+/**
+ * Deserializer of the {@link Double} type.
+ */
+class DoubleDeserializer extends AbstractNumberDeserializer<Double> {
+
+    DoubleDeserializer(TypeDeserializerBuilder builder) {
+        super(builder, false);
+    }
+
+    @Override
+    Double parseNumberValue(String value) {
+        return Double.parseDouble(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/DurationDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/DurationDeserializer.java
new file mode 100644
index 0000000..438b4bd
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/DurationDeserializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.time.Duration;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Duration} type.
+ */
+class DurationDeserializer extends TypeDeserializer {
+
+    DurationDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    public Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return Duration.parse(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/EnumDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/EnumDeserializer.java
new file mode 100644
index 0000000..f32aeb8
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/EnumDeserializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Enum}.
+ */
+class EnumDeserializer extends TypeDeserializer {
+
+    EnumDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return Enum.valueOf((Class<Enum>) rType, value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/FloatDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/FloatDeserializer.java
new file mode 100644
index 0000000..83dde61
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/FloatDeserializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+/**
+ * Deserializer of the {@link Float} type.
+ */
+class FloatDeserializer extends AbstractNumberDeserializer<Float> {
+
+    FloatDeserializer(TypeDeserializerBuilder builder) {
+        super(builder, false);
+    }
+
+    @Override
+    Float parseNumberValue(String value) {
+        return Float.parseFloat(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/InstantDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/InstantDeserializer.java
new file mode 100644
index 0000000..f00e521
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/InstantDeserializer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Deserializer of the {@link Instant} type.
+ */
+class InstantDeserializer extends AbstractDateDeserializer<Instant> {
+
+    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_INSTANT.withZone(UTC);
+
+    InstantDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected Instant fromInstant(Instant instant) {
+        return instant;
+    }
+
+    @Override
+    protected Instant parseDefault(String jsonValue, Locale locale) {
+        return Instant.from(DEFAULT_FORMATTER.withLocale(locale).parse(jsonValue));
+    }
+
+    @Override
+    protected Instant parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return Instant.from(getZonedFormatter(formatter).parse(jsonValue));
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/IntegerDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/IntegerDeserializer.java
new file mode 100644
index 0000000..73a5037
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/IntegerDeserializer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Integer} type.
+ */
+class IntegerDeserializer extends AbstractNumberDeserializer<Integer> {
+
+    IntegerDeserializer(TypeDeserializerBuilder builder) {
+        super(builder, true);
+    }
+
+    @Override
+    Integer parseNumberValue(String value) {
+        return Integer.parseInt(value);
+    }
+
+    @Override
+    Object deserializeNumberValue(JsonParser value, DeserializationContextImpl context, Type rType) {
+        return value.getInt();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/JsonValueDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/JsonValueDeserializer.java
new file mode 100644
index 0000000..ea97c42
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/JsonValueDeserializer.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserializer of the {@link JsonValue} type.
+ */
+class JsonValueDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<Object> delegate;
+    private final JsonValue nullValue;
+
+    JsonValueDeserializer(TypeDeserializerBuilder builder, JsonValue nullValue) {
+        this.delegate = builder.getDelegate();
+        this.nullValue = nullValue;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        JsonParser.Event last = context.getLastValueEvent();
+        return delegate.deserialize(deserializeValue(last, value), context);
+    }
+
+    private JsonValue deserializeValue(JsonParser.Event last, JsonParser parser) {
+        switch (last) {
+        case VALUE_TRUE:
+            return JsonValue.TRUE;
+        case VALUE_FALSE:
+            return JsonValue.FALSE;
+        case VALUE_NULL:
+            return nullValue;
+        case VALUE_STRING:
+        case VALUE_NUMBER:
+            return parser.getValue();
+        case START_OBJECT:
+            return parser.getObject();
+        case START_ARRAY:
+            return parser.getArray();
+        default:
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Unknown JSON value: " + last));
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateDeserializer.java
new file mode 100644
index 0000000..a38e727
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateDeserializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Deserializer of the {@link LocalDate} type.
+ */
+class LocalDateDeserializer extends AbstractDateDeserializer<LocalDate> {
+
+    LocalDateDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected LocalDate fromInstant(Instant instant) {
+        return instant.atZone(UTC).toLocalDate();
+    }
+
+    @Override
+    protected LocalDate parseDefault(String jsonValue, Locale locale) {
+        return LocalDate.parse(jsonValue, DateTimeFormatter.ISO_LOCAL_DATE.withLocale(locale));
+    }
+
+    @Override
+    protected LocalDate parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return LocalDate.parse(jsonValue, formatter);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateTimeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateTimeDeserializer.java
new file mode 100644
index 0000000..111a1e0
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateTimeDeserializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Deserializer of the {@link LocalDateTime} type.
+ */
+class LocalDateTimeDeserializer extends AbstractDateDeserializer<LocalDateTime> {
+
+    LocalDateTimeDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected LocalDateTime fromInstant(Instant instant) {
+        return LocalDateTime.ofInstant(instant, UTC);
+    }
+
+    @Override
+    protected LocalDateTime parseDefault(String jsonValue, Locale locale) {
+        return LocalDateTime.parse(jsonValue, DateTimeFormatter.ISO_LOCAL_DATE_TIME.withLocale(locale));
+    }
+
+    @Override
+    protected LocalDateTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return LocalDateTime.parse(jsonValue, formatter);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalTimeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalTimeDeserializer.java
new file mode 100644
index 0000000..979e2a1
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalTimeDeserializer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserializer of the {@link LocalTime} type.
+ */
+class LocalTimeDeserializer extends AbstractDateDeserializer<LocalTime> {
+
+    LocalTimeDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected LocalTime fromInstant(Instant instant) {
+        throw new JsonbException(Messages.getMessage(MessageKeys.TIME_TO_EPOCH_MILLIS_ERROR, LocalTime.class.getSimpleName()));
+    }
+
+    @Override
+    protected LocalTime parseDefault(String jsonValue, Locale locale) {
+        return LocalTime.parse(jsonValue, DateTimeFormatter.ISO_LOCAL_TIME.withLocale(locale));
+    }
+
+    @Override
+    protected LocalTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return LocalTime.parse(jsonValue, formatter);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/LongDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/LongDeserializer.java
new file mode 100644
index 0000000..24f0eb7
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/LongDeserializer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Long} type.
+ */
+class LongDeserializer extends AbstractNumberDeserializer<Long> {
+
+    LongDeserializer(TypeDeserializerBuilder builder) {
+        super(builder, true);
+    }
+
+    @Override
+    Long parseNumberValue(String value) {
+        return Long.parseLong(value);
+    }
+
+    @Override
+    Object deserializeNumberValue(JsonParser value, DeserializationContextImpl context, Type rType) {
+        return value.getLong();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/MonthDayTypeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/MonthDayTypeDeserializer.java
new file mode 100644
index 0000000..89fb1d1
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/MonthDayTypeDeserializer.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.MonthDay;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Deserializer of the {@link MonthDay} type.
+ */
+class MonthDayTypeDeserializer extends AbstractDateDeserializer<MonthDay> {
+
+    private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("--MM-dd").withZone(UTC);
+
+    MonthDayTypeDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected MonthDay fromInstant(Instant instant) {
+        return MonthDay.from(instant.atZone(UTC));
+    }
+
+    @Override
+    protected MonthDay parseDefault(String jsonValue, Locale locale) {
+        return MonthDay.parse(jsonValue, DEFAULT_FORMAT.withLocale(locale));
+    }
+
+    @Override
+    protected MonthDay parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return MonthDay.parse(jsonValue, formatter);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/NumberDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/NumberDeserializer.java
new file mode 100644
index 0000000..a21f444
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/NumberDeserializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Number} type.
+ */
+class NumberDeserializer extends TypeDeserializer {
+
+    NumberDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return new BigDecimal(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/ObjectTypeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ObjectTypeDeserializer.java
new file mode 100644
index 0000000..3f3d7aa
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ObjectTypeDeserializer.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.List;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+
+/**
+ * Deserializer of the {@link Object} type.
+ */
+class ObjectTypeDeserializer implements ModelDeserializer<JsonParser> {
+
+    private static final Type LIST = List.class;
+
+    private final ModelDeserializer<Object> delegate;
+    private final Class<?> mapClass;
+
+    ObjectTypeDeserializer(TypeDeserializerBuilder builder) {
+        this.delegate = builder.getDelegate();
+        this.mapClass = builder.getConfigProperties().getDefaultMapImplType();
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        Object toSet;
+        switch (context.getLastValueEvent()) {
+        case VALUE_TRUE:
+            toSet = Boolean.TRUE;
+            break;
+        case VALUE_FALSE:
+            toSet = Boolean.FALSE;
+            break;
+        case VALUE_NUMBER:
+            toSet = new BigDecimal(value.getString());
+            break;
+        case KEY_NAME:
+        case VALUE_STRING:
+            toSet = value.getString();
+            break;
+        case START_OBJECT:
+            DeserializationContextImpl newContext = new DeserializationContextImpl(context);
+            toSet = newContext.deserialize(mapClass, value);
+            break;
+        case START_ARRAY:
+            DeserializationContextImpl newContext1 = new DeserializationContextImpl(context);
+            toSet = newContext1.deserialize(LIST, value);
+            break;
+        default:
+            throw new JsonbException("Unexpected event: " + context.getLastValueEvent());
+        }
+        return delegate.deserialize(toSet, context);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/OffsetDateTimeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OffsetDateTimeDeserializer.java
new file mode 100644
index 0000000..5ae090b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OffsetDateTimeDeserializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+import java.util.logging.Logger;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserializer of the {@link OffsetDateTime} type.
+ */
+class OffsetDateTimeDeserializer extends AbstractDateDeserializer<OffsetDateTime> {
+
+    private static final Logger LOGGER = Logger.getLogger(OffsetDateTimeDeserializer.class.getName());
+
+    OffsetDateTimeDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    /**
+     * fromInstant is called only in case {@link jakarta.json.bind.annotation.JsonbDateFormat} is TIME_IN_MILLIS,
+     * which doesn't make much sense for usage with OffsetDateTime.
+     */
+    @Override
+    protected OffsetDateTime fromInstant(Instant instant) {
+        LOGGER.warning(Messages.getMessage(MessageKeys.OFFSET_DATE_TIME_FROM_MILLIS, OffsetDateTime.class.getSimpleName(), UTC));
+        return OffsetDateTime.ofInstant(instant, UTC);
+    }
+
+    @Override
+    protected OffsetDateTime parseDefault(String jsonValue, Locale locale) {
+        return OffsetDateTime.parse(jsonValue, DateTimeFormatter.ISO_OFFSET_DATE_TIME.withLocale(locale));
+    }
+
+    @Override
+    protected OffsetDateTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return OffsetDateTime.parse(jsonValue, formatter);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/OffsetTimeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OffsetTimeDeserializer.java
new file mode 100644
index 0000000..7d9d14c
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OffsetTimeDeserializer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.OffsetTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserializer of the {@link OffsetTime} type.
+ */
+class OffsetTimeDeserializer extends AbstractDateDeserializer<OffsetTime> {
+
+    OffsetTimeDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected OffsetTime fromInstant(Instant instant) {
+        throw new JsonbException(Messages.getMessage(MessageKeys.TIME_TO_EPOCH_MILLIS_ERROR, OffsetTime.class.getSimpleName()));
+    }
+
+    @Override
+    protected OffsetTime parseDefault(String jsonValue, Locale locale) {
+        return OffsetTime.parse(jsonValue, DateTimeFormatter.ISO_OFFSET_TIME.withLocale(locale));
+    }
+
+    @Override
+    protected OffsetTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return OffsetTime.parse(jsonValue, formatter);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalDoubleDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalDoubleDeserializer.java
new file mode 100644
index 0000000..d898a43
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalDoubleDeserializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.util.OptionalDouble;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+
+/**
+ * Deserializer of the {@link OptionalDouble} type.
+ */
+class OptionalDoubleDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> extractor;
+    private final ModelDeserializer<Object> nullValueDelegate;
+
+    OptionalDoubleDeserializer(ModelDeserializer<JsonParser> extractor, ModelDeserializer<Object> nullValueDelegate) {
+        this.extractor = extractor;
+        this.nullValueDelegate = nullValueDelegate;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        if (context.getLastValueEvent() == JsonParser.Event.VALUE_NULL) {
+            return nullValueDelegate.deserialize(OptionalDouble.empty(), context);
+        }
+        OptionalDouble optional = OptionalDouble.of((Double) extractor.deserialize(value, context));
+        return nullValueDelegate.deserialize(optional, context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalIntDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalIntDeserializer.java
new file mode 100644
index 0000000..261d8c6
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalIntDeserializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.util.OptionalInt;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+
+/**
+ * Deserializer of the {@link OptionalInt} type.
+ */
+class OptionalIntDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> extractor;
+    private final ModelDeserializer<Object> delegate;
+
+    OptionalIntDeserializer(ModelDeserializer<JsonParser> extractor, ModelDeserializer<Object> delegate) {
+        this.extractor = extractor;
+        this.delegate = delegate;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        if (context.getLastValueEvent() == JsonParser.Event.VALUE_NULL) {
+            return delegate.deserialize(OptionalInt.empty(), context);
+        }
+        OptionalInt optional = OptionalInt.of((Integer) extractor.deserialize(value, context));
+        return delegate.deserialize(optional, context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalLongDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalLongDeserializer.java
new file mode 100644
index 0000000..1501cca
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/OptionalLongDeserializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.util.OptionalLong;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+
+/**
+ * Deserializer of the {@link OptionalLong} type.
+ */
+class OptionalLongDeserializer implements ModelDeserializer<JsonParser> {
+
+    private final ModelDeserializer<JsonParser> extractor;
+    private final ModelDeserializer<Object> nullValueDelegate;
+
+    OptionalLongDeserializer(ModelDeserializer<JsonParser> extractor, ModelDeserializer<Object> nullValueDelegate) {
+        this.extractor = extractor;
+        this.nullValueDelegate = nullValueDelegate;
+    }
+
+    @Override
+    public Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        if (context.getLastValueEvent() == JsonParser.Event.VALUE_NULL) {
+            return nullValueDelegate.deserialize(OptionalLong.empty(), context);
+        }
+        OptionalLong optional = OptionalLong.of((Long) extractor.deserialize(value, context));
+        return nullValueDelegate.deserialize(optional, context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/PathDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/PathDeserializer.java
new file mode 100644
index 0000000..0bae35d
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/PathDeserializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.nio.file.Paths;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link java.nio.file.Path} type.
+ */
+class PathDeserializer extends TypeDeserializer {
+
+    PathDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return Paths.get(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/PeriodDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/PeriodDeserializer.java
new file mode 100644
index 0000000..2fb4aad
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/PeriodDeserializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.time.Period;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Period} type.
+ */
+class PeriodDeserializer extends TypeDeserializer {
+
+    PeriodDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return Period.parse(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/ShortDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ShortDeserializer.java
new file mode 100644
index 0000000..01e2cf2
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ShortDeserializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+/**
+ * Deserializer of the {@link Short} type.
+ */
+class ShortDeserializer extends AbstractNumberDeserializer<Short> {
+
+    ShortDeserializer(TypeDeserializerBuilder builder) {
+        super(builder, true);
+    }
+
+    @Override
+    Short parseNumberValue(String value) {
+        return Short.parseShort(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/SqlDateDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/SqlDateDeserializer.java
new file mode 100644
index 0000000..0ff1eab
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/SqlDateDeserializer.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.sql.Date;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link Date} type.
+ */
+public class SqlDateDeserializer extends AbstractDateDeserializer<Date> implements JsonbDeserializer<Date> {
+
+    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_DATE.withZone(UTC);
+
+    SqlDateDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    /**
+     * Create new instance.
+     */
+    public SqlDateDeserializer() {
+        super(Date.class);
+    }
+
+    @Override
+    protected Date fromInstant(Instant instant) {
+        return new Date(instant.toEpochMilli());
+    }
+
+    @Override
+    protected Date parseDefault(String jsonValue, Locale locale) {
+        return Date.valueOf(LocalDate.parse(jsonValue, DEFAULT_FORMATTER.withLocale(locale)));
+    }
+
+    @Override
+    protected Date parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return Date.valueOf(LocalDate.parse(jsonValue, formatter));
+    }
+
+    @Override
+    public Date deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        DeserializationContextImpl context = (DeserializationContextImpl) ctx;
+        return (Date) deserialize(parser.getString(), context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/SqlTimestampDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/SqlTimestampDeserializer.java
new file mode 100644
index 0000000..b0ffee8
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/SqlTimestampDeserializer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.util.Locale;
+
+/**
+ * Deserializer of the {@link Timestamp} type.
+ */
+class SqlTimestampDeserializer extends AbstractDateDeserializer<Timestamp> {
+
+    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_DATE_TIME.withZone(UTC);
+
+    SqlTimestampDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected Timestamp fromInstant(Instant instant) {
+        return Timestamp.from(instant);
+    }
+
+    @Override
+    protected Timestamp parseDefault(String jsonValue, Locale locale) {
+        final TemporalAccessor parsed = DEFAULT_FORMATTER.withLocale(locale).parse(jsonValue);
+        return Timestamp.from(getInstant(parsed));
+    }
+
+    @Override
+    protected Timestamp parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        final TemporalAccessor parsed = getZonedFormatter(formatter).parse(jsonValue);
+        return Timestamp.from(getInstant(parsed));
+    }
+
+    private Instant getInstant(TemporalAccessor parsed) {
+        LocalDateTime local = LocalDateTime.from(parsed);
+        return local.atZone(ZoneId.of("UTC")).toInstant();
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/StringDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/StringDeserializer.java
new file mode 100644
index 0000000..f1061cd
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/StringDeserializer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.JsonbConfigProperties;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserializer of the {@link String} type.
+ */
+class StringDeserializer extends TypeDeserializer {
+
+    StringDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    public Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        JsonbConfigProperties config = context.getJsonbContext().getConfigProperties();
+        return checkIJson(value, config);
+    }
+
+    private String checkIJson(String value, JsonbConfigProperties config) {
+        if (config.isStrictIJson()) {
+            String newString = new String(value.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
+            if (!newString.equals(value)) {
+                throw new JsonbException(Messages.getMessage(MessageKeys.UNPAIRED_SURROGATE));
+            }
+        }
+        return value;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/TimeZoneDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TimeZoneDeserializer.java
new file mode 100644
index 0000000..b225114
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TimeZoneDeserializer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.zone.ZoneRulesException;
+import java.util.SimpleTimeZone;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserializer of the {@link java.util.TimeZone} type.
+ */
+class TimeZoneDeserializer extends TypeDeserializer {
+
+    TimeZoneDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        try {
+            final ZoneId zoneId = ZoneId.of(value);
+            final ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(zoneId);
+            return new SimpleTimeZone(zonedDateTime.getOffset().getTotalSeconds() * 1000, zoneId.getId());
+        } catch (ZoneRulesException e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.ZONE_PARSE_ERROR, value), e);
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializer.java
new file mode 100644
index 0000000..0c7354f
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializer.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+
+/**
+ * Base for all type deserializers.
+ */
+public abstract class TypeDeserializer implements ModelDeserializer<String> {
+
+    private final ModelDeserializer<Object> delegate;
+    private final Class<?> clazz;
+
+    TypeDeserializer(TypeDeserializerBuilder builder) {
+        this.delegate = builder.getDelegate();
+        this.clazz = builder.getClazz();
+    }
+
+    @Override
+    public final Object deserialize(String value, DeserializationContextImpl context) {
+        return delegate.deserialize(deserializeStringValue(value, context, clazz), context);
+    }
+
+    public final Object deserialize(boolean value, DeserializationContextImpl context) {
+        return delegate.deserialize(deserializeBooleanValue(value, context, clazz), context);
+    }
+
+    public final Object deserialize(JsonParser value, DeserializationContextImpl context) {
+        return delegate.deserialize(deserializeNumberValue(value, context, clazz), context);
+    }
+
+    abstract Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType);
+
+    Object deserializeBooleanValue(boolean value, DeserializationContextImpl context, Type rType) {
+        return deserializeStringValue(String.valueOf(value), context, rType);
+    }
+
+    Object deserializeNumberValue(JsonParser value, DeserializationContextImpl context, Type rType) {
+        return deserializeStringValue(value.getString(), context, rType);
+    }
+
+    Class<?> getType() {
+        return clazz;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializerBuilder.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializerBuilder.java
new file mode 100644
index 0000000..c15ee2f
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializerBuilder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.util.Objects;
+
+import org.eclipse.yasson.internal.JsonbConfigProperties;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+import org.eclipse.yasson.internal.model.customization.Customization;
+
+class TypeDeserializerBuilder {
+
+    private final Class<?> clazz;
+    private final Customization customization;
+    private final JsonbConfigProperties configProperties;
+    private final ModelDeserializer<Object> delegate;
+
+    TypeDeserializerBuilder(Class<?> clazz,
+                            Customization customization,
+                            JsonbConfigProperties configProperties,
+                            ModelDeserializer<Object> delegate) {
+        this.clazz = Objects.requireNonNull(clazz);
+        this.customization = customization == null ? ClassCustomization.empty() : customization;
+        this.configProperties = configProperties;
+        this.delegate = Objects.requireNonNull(delegate);
+    }
+
+    public Class<?> getClazz() {
+        return clazz;
+    }
+
+    public JsonbConfigProperties getConfigProperties() {
+        return configProperties;
+    }
+
+    public ModelDeserializer<Object> getDelegate() {
+        return delegate;
+    }
+
+    public Customization getCustomization() {
+        return customization;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializers.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializers.java
new file mode 100644
index 0000000..de1f7e0
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/TypeDeserializers.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Path;
+import java.sql.Timestamp;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Period;
+import java.time.YearMonth;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.Set;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.function.Function;
+
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.JsonbConfigProperties;
+import org.eclipse.yasson.internal.deserializer.JustReturn;
+import org.eclipse.yasson.internal.deserializer.ModelDeserializer;
+import org.eclipse.yasson.internal.deserializer.NullCheckDeserializer;
+import org.eclipse.yasson.internal.deserializer.PositionChecker;
+import org.eclipse.yasson.internal.deserializer.ValueExtractor;
+import org.eclipse.yasson.internal.model.customization.Customization;
+
+import static org.eclipse.yasson.internal.BuiltInTypes.isClassAvailable;
+
+/**
+ * Specific type deserializers.
+ */
+public class TypeDeserializers {
+
+    private static final Map<Class<?>, Function<TypeDeserializerBuilder, TypeDeserializer>> DESERIALIZERS =
+            new HashMap<>();
+    private static final Map<Class<?>, Class<?>> OPTIONAL_TYPES = new HashMap<>();
+
+    static {
+        DESERIALIZERS.put(BigInteger.class, BigIntegerDeserializer::new);
+        DESERIALIZERS.put(BigDecimal.class, BigDecimalDeserializer::new);
+        DESERIALIZERS.put(Boolean.class, BooleanDeserializer::new);
+        DESERIALIZERS.put(Boolean.TYPE, BooleanDeserializer::new);
+        DESERIALIZERS.put(Byte.class, ByteDeserializer::new);
+        DESERIALIZERS.put(Byte.TYPE, ByteDeserializer::new);
+        DESERIALIZERS.put(Calendar.class, CalendarDeserializer::new);
+        DESERIALIZERS.put(Character.TYPE, CharDeserializer::new);
+        DESERIALIZERS.put(Character.class, CharDeserializer::new);
+        DESERIALIZERS.put(Date.class, DateDeserializer::new);
+        DESERIALIZERS.put(Double.class, DoubleDeserializer::new);
+        DESERIALIZERS.put(Double.TYPE, DoubleDeserializer::new);
+        DESERIALIZERS.put(Duration.class, DurationDeserializer::new);
+        DESERIALIZERS.put(Float.class, FloatDeserializer::new);
+        DESERIALIZERS.put(Float.TYPE, FloatDeserializer::new);
+        DESERIALIZERS.put(GregorianCalendar.class, CalendarDeserializer::new);
+        DESERIALIZERS.put(Instant.class, InstantDeserializer::new);
+        DESERIALIZERS.put(Integer.class, IntegerDeserializer::new);
+        DESERIALIZERS.put(Integer.TYPE, IntegerDeserializer::new);
+        DESERIALIZERS.put(LocalDate.class, LocalDateDeserializer::new);
+        DESERIALIZERS.put(LocalDateTime.class, LocalDateTimeDeserializer::new);
+        DESERIALIZERS.put(LocalTime.class, LocalTimeDeserializer::new);
+        DESERIALIZERS.put(Long.class, LongDeserializer::new);
+        DESERIALIZERS.put(Long.TYPE, LongDeserializer::new);
+        DESERIALIZERS.put(MonthDay.class, MonthDayTypeDeserializer::new);
+        DESERIALIZERS.put(Number.class, NumberDeserializer::new);
+        DESERIALIZERS.put(OffsetDateTime.class, OffsetDateTimeDeserializer::new);
+        DESERIALIZERS.put(OffsetTime.class, OffsetTimeDeserializer::new);
+        DESERIALIZERS.put(Path.class, PathDeserializer::new);
+        DESERIALIZERS.put(Period.class, PeriodDeserializer::new);
+        DESERIALIZERS.put(Short.class, ShortDeserializer::new);
+        DESERIALIZERS.put(Short.TYPE, ShortDeserializer::new);
+        DESERIALIZERS.put(String.class, StringDeserializer::new);
+        DESERIALIZERS.put(SimpleTimeZone.class, TimeZoneDeserializer::new);
+        DESERIALIZERS.put(TimeZone.class, TimeZoneDeserializer::new);
+        DESERIALIZERS.put(URI.class, UriDeserializer::new);
+        DESERIALIZERS.put(URL.class, UrlDeserializer::new);
+        DESERIALIZERS.put(UUID.class, UuidDeserializer::new);
+        if (isClassAvailable("javax.xml.datatype.XMLGregorianCalendar")) {
+            DESERIALIZERS.put(XMLGregorianCalendar.class, XmlGregorianCalendarDeserializer::new);
+        }
+        DESERIALIZERS.put(YearMonth.class, YearMonthTypeDeserializer::new);
+        DESERIALIZERS.put(ZonedDateTime.class, ZonedDateTimeDeserializer::new);
+        DESERIALIZERS.put(ZoneId.class, ZoneIdDeserializer::new);
+        DESERIALIZERS.put(ZoneOffset.class, ZoneOffsetDeserializer::new);
+        if (isClassAvailable("java.sql.Date")) {
+            DESERIALIZERS.put(java.sql.Date.class, SqlDateDeserializer::new);
+            DESERIALIZERS.put(Timestamp.class, SqlTimestampDeserializer::new);
+        }
+
+        OPTIONAL_TYPES.put(OptionalLong.class, Long.class);
+        OPTIONAL_TYPES.put(OptionalInt.class, Integer.class);
+        OPTIONAL_TYPES.put(OptionalDouble.class, Double.class);
+    }
+
+    private TypeDeserializers() {
+        throw new IllegalStateException("Utility classes cannot be instantiated");
+    }
+
+    /**
+     * Return deserializer for the given type.
+     *
+     * @param clazz         type to create deserializer for
+     * @param customization type customization
+     * @param properties    config properties
+     * @param delegate      delegate to be called by the created deserializer
+     * @param events        expected parser events at the beginning when deserializing the type
+     * @return type deserializer
+     */
+    public static ModelDeserializer<JsonParser> getTypeDeserializer(Class<?> clazz,
+                                                                    Customization customization,
+                                                                    JsonbConfigProperties properties,
+                                                                    ModelDeserializer<Object> delegate,
+                                                                    Set<JsonParser.Event> events) {
+        JsonParser.Event[] eventArray = events.toArray(new JsonParser.Event[0]);
+        if (OPTIONAL_TYPES.containsKey(clazz)) {
+            Class<?> optionalType = OPTIONAL_TYPES.get(clazz);
+            TypeDeserializerBuilder builder = new TypeDeserializerBuilder(optionalType,
+                                                                          customization,
+                                                                          properties,
+                                                                          JustReturn.instance());
+            ValueExtractor valueExtractor = new ValueExtractor(DESERIALIZERS.get(optionalType).apply(builder));
+            PositionChecker positionChecker = new PositionChecker(valueExtractor, clazz, eventArray);
+            if (OptionalLong.class.equals(clazz)) {
+                return new OptionalLongDeserializer(positionChecker, delegate);
+            } else if (OptionalInt.class.equals(clazz)) {
+                return new OptionalIntDeserializer(positionChecker, delegate);
+            } else if (OptionalDouble.class.equals(clazz)) {
+                return new OptionalDoubleDeserializer(positionChecker, delegate);
+            } else {
+                throw new JsonbException("Unsupported Optional type for deserialization: " + clazz);
+            }
+        }
+
+        TypeDeserializerBuilder builder = new TypeDeserializerBuilder(clazz, customization, properties, delegate);
+        if (DESERIALIZERS.containsKey(clazz)) {
+            ValueExtractor valueExtractor = new ValueExtractor(DESERIALIZERS.get(clazz).apply(builder));
+            return new NullCheckDeserializer(new PositionChecker(valueExtractor, clazz, eventArray), delegate);
+        }
+
+        if (JsonValue.class.equals(clazz)) {
+            return new JsonValueDeserializer(builder, JsonValue.NULL);
+        } else if (JsonValue.class.isAssignableFrom(clazz)) {
+            return new JsonValueDeserializer(builder, null);
+        }
+        ModelDeserializer<JsonParser> deserializer = assignableCases(builder, eventArray);
+        if (deserializer != null) {
+            return new NullCheckDeserializer(deserializer, delegate);
+        }
+        return null;
+    }
+
+    private static ModelDeserializer<JsonParser> assignableCases(TypeDeserializerBuilder builder,
+                                                                 JsonParser.Event[] checker) {
+        if (Enum.class.isAssignableFrom(builder.getClazz())) {
+            return new PositionChecker(new ValueExtractor(new EnumDeserializer(builder)),
+                                       builder.getClazz(),
+                                       checker);
+        } else if (Object.class.equals(builder.getClazz())) {
+            return new ObjectTypeDeserializer(builder);
+        }
+        return null;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/UriDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/UriDeserializer.java
new file mode 100644
index 0000000..26ea0fb
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/UriDeserializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.net.URI;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link URI} type.
+ */
+class UriDeserializer extends TypeDeserializer {
+
+    UriDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return URI.create(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/UrlDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/UrlDeserializer.java
new file mode 100644
index 0000000..54cd2f3
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/UrlDeserializer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link URL} type.
+ */
+class UrlDeserializer extends TypeDeserializer {
+
+    UrlDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        URL url = null;
+        try {
+            url = new URL(value);
+        } catch (MalformedURLException e) {
+            e.printStackTrace();
+        }
+        return url;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/UuidDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/UuidDeserializer.java
new file mode 100644
index 0000000..7324849
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/UuidDeserializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.util.UUID;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link UUID} type.
+ */
+class UuidDeserializer extends TypeDeserializer {
+
+    UuidDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return UUID.fromString(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/XmlGregorianCalendarDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/XmlGregorianCalendarDeserializer.java
new file mode 100644
index 0000000..bb246bc
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/XmlGregorianCalendarDeserializer.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalQueries;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserializer of the {@link XMLGregorianCalendar} type.
+ */
+class XmlGregorianCalendarDeserializer extends AbstractDateDeserializer<XMLGregorianCalendar> {
+
+    private static final LocalTime ZERO_LOCAL_TIME = LocalTime.parse("00:00:00");
+
+    private final Calendar calendarTemplate;
+    private final DatatypeFactory datatypeFactory;
+
+    XmlGregorianCalendarDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+        this.calendarTemplate = new GregorianCalendar();
+        this.calendarTemplate.clear();
+        this.calendarTemplate.setTimeZone(TimeZone.getTimeZone(UTC));
+        try {
+            this.datatypeFactory = DatatypeFactory.newInstance();
+        } catch (DatatypeConfigurationException e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.DATATYPE_FACTORY_CREATION_FAILED), e);
+        }
+    }
+
+    @Override
+    protected XMLGregorianCalendar fromInstant(Instant instant) {
+        final GregorianCalendar calendar = (GregorianCalendar) calendarTemplate.clone();
+        calendar.setTimeInMillis(instant.toEpochMilli());
+        return datatypeFactory.newXMLGregorianCalendar(calendar);
+    }
+
+    @Override
+    protected XMLGregorianCalendar parseDefault(String jsonValue, Locale locale) {
+        DateTimeFormatter formatter = jsonValue.contains("T")
+                ? DateTimeFormatter.ISO_DATE_TIME
+                : DateTimeFormatter.ISO_DATE;
+        return parseWithFormatter(jsonValue, formatter.withLocale(locale));
+    }
+
+    @Override
+    protected XMLGregorianCalendar parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        final TemporalAccessor parsed = formatter.parse(jsonValue);
+        LocalTime time = parsed.query(TemporalQueries.localTime());
+        ZoneId zone = parsed.query(TemporalQueries.zone());
+        if (zone == null) {
+            zone = UTC;
+        }
+        if (time == null) {
+            time = ZERO_LOCAL_TIME;
+        }
+        ZonedDateTime result = LocalDate.from(parsed).atTime(time).atZone(zone);
+        return datatypeFactory.newXMLGregorianCalendar(GregorianCalendar.from(result));
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/YearMonthTypeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/YearMonthTypeDeserializer.java
new file mode 100644
index 0000000..30f1d36
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/YearMonthTypeDeserializer.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Deserializer of the {@link YearMonth} type.
+ */
+class YearMonthTypeDeserializer extends AbstractDateDeserializer<YearMonth> {
+
+    private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM").withZone(UTC);
+
+    YearMonthTypeDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected YearMonth fromInstant(Instant instant) {
+        return YearMonth.from(instant.atZone(UTC));
+    }
+
+    @Override
+    protected YearMonth parseDefault(String jsonValue, Locale locale) {
+        return YearMonth.parse(jsonValue, DEFAULT_FORMAT.withLocale(locale));
+    }
+
+    @Override
+    protected YearMonth parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return YearMonth.parse(jsonValue, formatter);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZoneIdDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZoneIdDeserializer.java
new file mode 100644
index 0000000..4ece9a0
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZoneIdDeserializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.time.ZoneId;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link ZoneId} type.
+ */
+class ZoneIdDeserializer extends TypeDeserializer {
+
+    ZoneIdDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return ZoneId.of(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZoneOffsetDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZoneOffsetDeserializer.java
new file mode 100644
index 0000000..a4eeb73
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZoneOffsetDeserializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.lang.reflect.Type;
+import java.time.ZoneOffset;
+
+import org.eclipse.yasson.internal.DeserializationContextImpl;
+
+/**
+ * Deserializer of the {@link ZoneOffset} type.
+ */
+class ZoneOffsetDeserializer extends TypeDeserializer {
+
+    ZoneOffsetDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    Object deserializeStringValue(String value, DeserializationContextImpl context, Type rType) {
+        return ZoneOffset.of(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZonedDateTimeDeserializer.java b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZonedDateTimeDeserializer.java
new file mode 100644
index 0000000..ba4db98
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/deserializer/types/ZonedDateTimeDeserializer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.deserializer.types;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+import java.util.logging.Logger;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Deserializer of the {@link ZonedDateTime} type.
+ */
+class ZonedDateTimeDeserializer extends AbstractDateDeserializer<ZonedDateTime> {
+
+    private static final Logger LOGGER = Logger.getLogger(ZonedDateTimeDeserializer.class.getName());
+
+    ZonedDateTimeDeserializer(TypeDeserializerBuilder builder) {
+        super(builder);
+    }
+
+    /**
+     * fromInstant is called only in case {@link jakarta.json.bind.annotation.JsonbDateFormat} is TIME_IN_MILLIS,
+     * which doesn't make much sense for usage with ZonedDateTime.
+     */
+    @Override
+    protected ZonedDateTime fromInstant(Instant instant) {
+        LOGGER.warning(Messages.getMessage(MessageKeys.OFFSET_DATE_TIME_FROM_MILLIS, ZonedDateTime.class.getSimpleName(), UTC));
+        return ZonedDateTime.ofInstant(instant, UTC);
+    }
+
+    @Override
+    protected ZonedDateTime parseDefault(String jsonValue, Locale locale) {
+        return ZonedDateTime.parse(jsonValue, DateTimeFormatter.ISO_ZONED_DATE_TIME.withLocale(locale));
+    }
+
+    @Override
+    protected ZonedDateTime parseWithFormatter(String jsonValue, DateTimeFormatter formatter) {
+        return ZonedDateTime.parse(jsonValue, getZonedFormatter(formatter));
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayBuilder.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayBuilder.java
new file mode 100644
index 0000000..2a13555
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayBuilder.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.jsonstructure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import jakarta.json.spi.JsonProvider;
+
+/**
+ * Builds {@link JsonArray}. Delegates to {@link jakarta.json.JsonArrayBuilder}.
+ */
+class JsonArrayBuilder extends JsonStructureBuilder {
+
+    private final jakarta.json.JsonArrayBuilder arrayBuilder;
+
+    /**
+     * Create instance with cached provider.
+     *
+     * @param provider Json provider to create JsonArrayBuilder on.
+     */
+    JsonArrayBuilder(JsonProvider provider) {
+        this.arrayBuilder = provider.createArrayBuilder();
+    }
+
+    @Override
+    JsonArray build() {
+        return arrayBuilder.build();
+    }
+
+    @Override
+    void write(JsonValue value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(String value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(BigDecimal value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(BigInteger value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(int value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(long value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(double value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void write(boolean value) {
+        arrayBuilder.add(value);
+    }
+
+    @Override
+    void writeNull() {
+        arrayBuilder.addNull();
+    }
+
+    @Override
+    void put(JsonStructure structure) {
+        arrayBuilder.add(structure);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayIterator.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayIterator.java
new file mode 100644
index 0000000..1b26ca8
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonArrayIterator.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.jsonstructure;
+
+import java.util.Iterator;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Iterates over {@link JsonArray}.
+ */
+public class JsonArrayIterator extends JsonStructureIterator {
+
+    private final Iterator<JsonValue> valueIterator;
+
+    private JsonValue currentValue;
+
+    /**
+     * Creates new array iterator.
+     *
+     * @param jsonArray json array
+     */
+    public JsonArrayIterator(JsonArray jsonArray) {
+        this.valueIterator = jsonArray.iterator();
+    }
+
+    /**
+     * After {@link JsonParser.Event} END_ARRAY is returned from next() iterator is removed from the stack.
+     *
+     * @return always true
+     */
+    @Override
+    public boolean hasNext() {
+        return true;
+    }
+
+    @Override
+    public JsonParser.Event next() {
+        if (valueIterator.hasNext()) {
+            currentValue = valueIterator.next();
+            return getValueEvent(currentValue);
+        }
+        return JsonParser.Event.END_ARRAY;
+    }
+
+    @Override
+    JsonValue getValue() {
+        return currentValue;
+    }
+
+    @Override
+    JsonbException createIncompatibleValueError() {
+        return new JsonbException(Messages.getMessage(MessageKeys.NUMBER_INCOMPATIBLE_VALUE_TYPE_ARRAY,
+                                                      getValue().getValueType()));
+    }
+
+    @Override
+    String getString() {
+        if (currentValue instanceof JsonString) {
+            return ((JsonString) currentValue).getString();
+        }
+        return currentValue.toString();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonGeneratorToStructureAdapter.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonGeneratorToStructureAdapter.java
new file mode 100644
index 0000000..5008e4b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonGeneratorToStructureAdapter.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.jsonstructure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.spi.JsonProvider;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Adapter for {@link JsonGenerator}, that builds a {@link JsonStructure} content tree instead of JSON text.
+ *
+ * Yasson and jsonb API components are using {@link JsonGenerator} as its output API.
+ * This adapter allows serialization of java content tree into {@link JsonStructure} using same components
+ * as when generating JSON text.
+ */
+public class JsonGeneratorToStructureAdapter implements JsonGenerator {
+
+    private final Deque<JsonStructureBuilder> builders;
+
+    private JsonStructure root;
+
+    private final JsonProvider provider;
+
+    /**
+     * Default constructor, jsonp builders are created internally.
+     *
+     * @param provider Cached json provider to create builders on.
+     */
+    public JsonGeneratorToStructureAdapter(JsonProvider provider) {
+        this.builders = new ArrayDeque<>();
+        this.provider = provider;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        builders.push(new JsonObjectBuilder(provider));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        getJsonObjectBuilder(name).writeKey(name);
+        builders.push(new JsonObjectBuilder(provider));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeKey(String name) {
+        getJsonObjectBuilder(name).writeKey(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        builders.push(new JsonArrayBuilder(provider));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        getJsonObjectBuilder(name).writeKey(name);
+        builders.push(new JsonArrayBuilder(provider));
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, String value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        getJsonObjectBuilder(name).write(name, value);
+        return this;
+    }
+
+    private JsonObjectBuilder getJsonObjectBuilder(String keyName) {
+        JsonStructureBuilder current = builders.peek();
+        if (!(current instanceof JsonObjectBuilder)) {
+            throw new JsonbException(Messages.getMessage(
+                    MessageKeys.INTERNAL_ERROR, "Can't write key [" + keyName + "] into " + current.getClass()
+                            + "because " + current.getClass() + " is not an instance of " + JsonObjectBuilder.class));
+        }
+        return (JsonObjectBuilder) current;
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        getJsonObjectBuilder(name).writeNull(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        JsonStructureBuilder builder = builders.pop();
+        JsonStructure structure = builder.build();
+        if (builders.isEmpty()) {
+            this.root = structure;
+        } else {
+            builders.peek().put(structure);
+        }
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(int value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(boolean value) {
+        builders.peek().write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        builders.peek().writeNull();
+        return this;
+    }
+
+    @Override
+    public void close() {
+        //noop
+    }
+
+    @Override
+    public void flush() {
+        //noop
+    }
+
+    /**
+     * Root structure wrapping all data.
+     *
+     * @return root JsonStructure.
+     */
+    public JsonStructure getRootStructure() {
+        return root;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectBuilder.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectBuilder.java
new file mode 100644
index 0000000..7a460e1
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectBuilder.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.jsonstructure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.spi.JsonProvider;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Builds {@link jakarta.json.JsonObject} delegates to {@link jakarta.json.JsonObjectBuilder}, caches key when
+ * written without a value.
+ */
+class JsonObjectBuilder extends JsonStructureBuilder {
+
+    private final jakarta.json.JsonObjectBuilder builder;
+
+    private String nextKey;
+
+    /**
+     * Create instance with cached provider.
+     *
+     * @param provider Json provider to create JsonObjectBuilder on.
+     */
+    JsonObjectBuilder(JsonProvider provider) {
+        this.builder = provider.createObjectBuilder();
+    }
+
+    @Override
+    JsonStructure build() {
+        return builder.build();
+    }
+
+    @Override
+    void put(JsonStructure structure) {
+        builder.add(getNextKey(), structure);
+    }
+
+    /**
+     * Puts another {@link JsonStructure} into current using provided key.
+     *
+     * @param name      key to put JsonStructure under.
+     * @param structure JsonStructure to put.
+     */
+    void put(String name, JsonStructure structure) {
+        builder.add(name, structure);
+    }
+
+    @Override
+    void write(JsonValue value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(String value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(BigDecimal value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(BigInteger value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(int value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(long value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(double value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void write(boolean value) {
+        builder.add(getNextKey(), value);
+    }
+
+    @Override
+    void writeNull() {
+        builder.addNull(getNextKey());
+    }
+
+    /**
+     * Write a key-value pair into current {@link jakarta.json.JsonObject}.
+     *
+     * @param name  Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, JsonValue value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link jakarta.json.JsonObject}.
+     *
+     * @param name  Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, String value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link jakarta.json.JsonObject}.
+     *
+     * @param name  Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, BigDecimal value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link jakarta.json.JsonObject}.
+     *
+     * @param name  Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, BigInteger value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link jakarta.json.JsonObject}.
+     *
+     * @param name  Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, int value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link jakarta.json.JsonObject}.
+     *
+     * @param name  Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, long value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link jakarta.json.JsonObject}.
+     *
+     * @param name  Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, double value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a key-value pair into current {@link jakarta.json.JsonObject}.
+     *
+     * @param name  Key name to write value with.
+     * @param value A value to write.
+     */
+    void write(String name, boolean value) {
+        builder.add(name, value);
+    }
+
+    /**
+     * Write a null into current {@link jakarta.json.JsonObject} with a given key.
+     *
+     * @param name Key name to write null with.
+     */
+    void writeNull(String name) {
+        builder.addNull(name);
+    }
+
+    /**
+     * Store a key for putting next value into built {@link jakarta.json.JsonObject}.
+     *
+     * @param key Key to store.
+     */
+    void writeKey(String key) {
+        this.nextKey = key;
+    }
+
+    private String getNextKey() {
+        if (nextKey == null) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR,
+                                                         "Can't write a value without key name"));
+        }
+        String key = nextKey;
+        nextKey = null;
+        return key;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java
new file mode 100644
index 0000000..4ba94c7
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonObjectIterator.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.jsonstructure;
+
+import java.util.Iterator;
+
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Iterates over {@link JsonObject} managing internal state.
+ */
+public class JsonObjectIterator extends JsonStructureIterator {
+
+    /**
+     * Location pointer.
+     */
+    public enum State {
+        /**
+         * Start of the object.
+         */
+        START,
+        /**
+         * Property key name.
+         */
+        KEY,
+        /**
+         * Property value.
+         */
+        VALUE,
+        /**
+         * End of the object.
+         */
+        END
+    }
+
+    private final JsonObject jsonObject;
+
+    private final Iterator<String> keyIterator;
+
+    private String currentKey;
+
+    private State state = State.START;
+
+    JsonObjectIterator(JsonObject jsonObject) {
+        this.jsonObject = jsonObject;
+        this.keyIterator = jsonObject.keySet().iterator();
+    }
+
+    private void nextKey() {
+        if (!keyIterator.hasNext()) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Object is empty"));
+        }
+        currentKey = keyIterator.next();
+    }
+
+    @Override
+    public JsonParser.Event next() {
+        switch (state) {
+        case START:
+            if (keyIterator.hasNext()) {
+                nextKey();
+                setState(JsonObjectIterator.State.KEY);
+                return JsonParser.Event.KEY_NAME;
+            } else {
+                setState(State.END);
+                return JsonParser.Event.END_OBJECT;
+            }
+        case KEY:
+            setState(JsonObjectIterator.State.VALUE);
+            JsonValue value = getValue();
+            return getValueEvent(value);
+        case VALUE:
+            if (keyIterator.hasNext()) {
+                nextKey();
+                setState(JsonObjectIterator.State.KEY);
+                return JsonParser.Event.KEY_NAME;
+            }
+            setState(State.END);
+            return JsonParser.Event.END_OBJECT;
+        default:
+            throw new JsonbException("Illegal state");
+        }
+
+    }
+
+    @Override
+    public boolean hasNext() {
+        //From the perspective of JsonParser not finished until END_OBJECT is being read.
+        return state != State.END;
+    }
+
+    /**
+     * {@link JsonValue} for current key.
+     *
+     * @return Current JsonValue.
+     */
+    public JsonValue getValue() {
+        if (state == State.START && currentKey == null) {
+            return jsonObject;
+        }
+        return jsonObject.get(currentKey);
+    }
+
+    @Override
+    String getString() {
+        if (state == JsonObjectIterator.State.KEY) {
+            return currentKey;
+        }
+        return super.getString();
+    }
+
+    @Override
+    JsonbException createIncompatibleValueError() {
+        return new JsonbException(Messages.getMessage(MessageKeys.NUMBER_INCOMPATIBLE_VALUE_TYPE_OBJECT,
+                                                      getValue().getValueType(),
+                                                      currentKey));
+    }
+
+    private void setState(State state) {
+        this.state = state;
+    }
+
+    /**
+     * Current key this iterator is pointing at.
+     *
+     * @return Current key.
+     */
+    public String getKey() {
+        return currentKey;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureBuilder.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureBuilder.java
new file mode 100644
index 0000000..f6c0de4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureBuilder.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.jsonstructure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+
+/**
+ * Grouping interface for {@link jakarta.json.JsonObject} and {@link jakarta.json.JsonArray} generation.
+ */
+abstract class JsonStructureBuilder {
+
+    /**
+     * Build and get constructed {@link JsonStructure}.
+     *
+     * @return JsonStructure result.
+     */
+    abstract JsonStructure build();
+
+    /**
+     * Puts another {@link JsonStructure} into current. If current is {@link jakarta.json.JsonObject} than last written
+     * key is used.
+     *
+     * @param structure
+     */
+    abstract void put(JsonStructure structure);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(JsonValue value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(String value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(BigDecimal value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(BigInteger value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(int value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(long value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(double value);
+
+    /**
+     * Write a value into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     *
+     * @param value A value to write.
+     */
+    abstract void write(boolean value);
+
+    /**
+     * Write null into current {@link JsonStructure}. If current is {@link jakarta.json.JsonObject}, last stored key
+     * by {@link JsonObjectBuilder#writeKey(String)} is used.
+     */
+    abstract void writeNull();
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java
new file mode 100644
index 0000000..1e402b5
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureIterator.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.jsonstructure;
+
+import java.util.Iterator;
+
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Iterates over {@link jakarta.json.JsonStructure}.
+ */
+abstract class JsonStructureIterator implements Iterator<JsonParser.Event> {
+
+    /**
+     * Get current {@link JsonValue}, that the parser is pointing on.
+     *
+     * @return JsonValue result.
+     */
+    abstract JsonValue getValue();
+
+    /**
+     * Creates an exception for throwing in case of current value type is not compatible with
+     * called getter return type.
+     *
+     * @return JsonbException with error description.
+     */
+    abstract JsonbException createIncompatibleValueError();
+
+    /**
+     * Check the type of current  {@link JsonValue} and return a string representing a value.
+     *
+     * @return String value for current JsonValue
+     */
+    String getString() {
+        JsonValue value = getValue();
+        if (value instanceof JsonString) {
+            return ((JsonString) value).getString();
+        } else {
+            return value.toString();
+        }
+    }
+
+    /**
+     * Convert {@link JsonValue} type to {@link JsonParser.Event}.
+     *
+     * @param value JsonValue
+     * @return JsonParser event
+     */
+    JsonParser.Event getValueEvent(JsonValue value) {
+        switch (value.getValueType()) {
+        case NUMBER:
+            return JsonParser.Event.VALUE_NUMBER;
+        case STRING:
+        case TRUE:
+        case FALSE:
+            return JsonParser.Event.VALUE_STRING;
+        case OBJECT:
+            return JsonParser.Event.START_OBJECT;
+        case ARRAY:
+            return JsonParser.Event.START_ARRAY;
+        case NULL:
+            return JsonParser.Event.VALUE_NULL;
+        default:
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR,
+                                                         "unknown json value: " + value.getValueType()));
+        }
+
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java
new file mode 100644
index 0000000..dfa9d64
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/jsonstructure/JsonStructureToParserAdapter.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2019, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.jsonstructure;
+
+import java.math.BigDecimal;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonNumber;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Adapter for {@link JsonParser}, that reads a {@link JsonStructure} content tree instead of JSON text.
+ *
+ * Yasson and jsonb API components are using {@link JsonParser} as its input API.
+ * This adapter allows deserialization of {@link JsonStructure} into java content tree using same components
+ * as when parsing JSON text.
+ */
+public class JsonStructureToParserAdapter implements JsonParser {
+
+    private Deque<JsonStructureIterator> iterators = new ArrayDeque<>();
+
+    private final JsonStructure rootStructure;
+
+    /**
+     * Creates new {@link JsonStructure} parser.
+     *
+     * @param structure json structure
+     */
+    public JsonStructureToParserAdapter(JsonStructure structure) {
+        this.rootStructure = structure;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return iterators.peek().hasNext();
+    }
+
+    @Override
+    public Event next() {
+        if (iterators.isEmpty()) {
+            if (rootStructure instanceof JsonObject) {
+                iterators.push(new JsonObjectIterator((JsonObject) rootStructure));
+                return Event.START_OBJECT;
+            } else if (rootStructure instanceof JsonArray) {
+                iterators.push(new JsonArrayIterator((JsonArray) rootStructure));
+                return Event.START_ARRAY;
+            }
+        }
+        JsonStructureIterator current = iterators.peek();
+        Event next = current.next();
+        if (next == Event.START_OBJECT) {
+            iterators.push(new JsonObjectIterator((JsonObject) current.getValue()));
+        } else if (next == Event.START_ARRAY) {
+            iterators.push(new JsonArrayIterator((JsonArray) current.getValue()));
+        } else if (next == Event.END_OBJECT || next == Event.END_ARRAY) {
+            iterators.pop();
+        }
+        return next;
+    }
+
+    @Override
+    public String getString() {
+        return iterators.peek().getString();
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        return getJsonNumberValue().isIntegral();
+    }
+
+    @Override
+    public int getInt() {
+        return getJsonNumberValue().intValueExact();
+    }
+
+    @Override
+    public long getLong() {
+        return getJsonNumberValue().longValueExact();
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        return getJsonNumberValue().bigDecimalValue();
+    }
+
+    @Override
+    public JsonObject getObject() {
+        JsonStructureIterator current = iterators.peek();
+        if (current instanceof JsonObjectIterator) {
+            //Remove child iterator as getObject() method contract says
+            iterators.pop();
+            return current.getValue().asJsonObject();
+        } else {
+            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Outside of object context"));
+        }
+    }
+
+    private JsonNumber getJsonNumberValue() {
+        JsonStructureIterator iterator = iterators.peek();
+        JsonValue value = iterator.getValue();
+        if (value.getValueType() != JsonValue.ValueType.NUMBER) {
+            throw iterator.createIncompatibleValueError();
+        }
+        return (JsonNumber) value;
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        throw new JsonbException("Operation not supported");
+    }
+
+    @Override
+    public void skipArray() {
+        if (!iterators.isEmpty()) {
+            JsonStructureIterator current = iterators.peek();
+            if (current instanceof JsonArrayIterator) {
+                iterators.pop();
+            }
+        }
+    }
+
+    @Override
+    public void skipObject() {
+        if (!iterators.isEmpty()) {
+            JsonStructureIterator current = iterators.peek();
+            if (current instanceof JsonObjectIterator) {
+                iterators.pop();
+            }
+        }
+    }
+
+    @Override
+    public void close() {
+        //noop
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/AnnotationTarget.java b/src/main/java/org/eclipse/yasson/internal/model/AnnotationTarget.java
new file mode 100644
index 0000000..566c9f2
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/AnnotationTarget.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+
+/**
+ * Represents the place in which a JSON annotation is applied. Some business functionalities are different based on whether
+ * annotation (e.g.
+ * {@link jakarta.json.bind.annotation.JsonbTransient}, {@link JsonbNumberFormatter}, etc
+ * .) is being applied on
+ * getter method, setter method or directly on the property.
+ */
+public enum AnnotationTarget {
+    /**
+     * Indicates annotation has been applied on class level.
+     */
+    CLASS,
+
+    /**
+     * Indicates annotation has been applied on property level.
+     */
+    PROPERTY,
+
+    /**
+     * Indicates annotation has been applied on the getter method of the property.
+     */
+    GETTER,
+
+    /**
+     * Indicates annotation has been applied on the setter method of the property.
+     */
+    SETTER
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/ClassModel.java b/src/main/java/org/eclipse/yasson/internal/model/ClassModel.java
new file mode 100644
index 0000000..8cdbdde
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/ClassModel.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+import jakarta.json.bind.config.PropertyNamingStrategy;
+
+import org.eclipse.yasson.internal.ClassMultiReleaseExtension;
+import org.eclipse.yasson.internal.ReflectionUtils;
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+import org.eclipse.yasson.internal.model.customization.StrategiesProvider;
+
+/**
+ * A model for Java class.
+ */
+public class ClassModel {
+
+    private final Class<?> clazz;
+
+    private final ClassCustomization classCustomization;
+
+    private final ClassModel parentClassModel;
+
+    private final AtomicBoolean isInitialized = new AtomicBoolean(false);
+
+    private Constructor<?> defaultConstructor;
+
+    /**
+     * A map of all class properties, including properties from superclasses. Used to access by name.
+     */
+    private Map<String, PropertyModel> properties;
+
+    /**
+     * Sorted properties according to sorting strategy. Used for serialization property ordering.
+     */
+    private PropertyModel[] sortedProperties;
+
+    private final PropertyNamingStrategy propertyNamingStrategy;
+
+    /**
+     * Gets a property model by default (non customized) name.
+     *
+     * @param name A name as parsed from field / getter / setter without annotation customizing.
+     * @return Property model.
+     */
+    public PropertyModel getPropertyModel(String name) {
+        return properties.get(name);
+    }
+
+    /**
+     * Create instance of class model.
+     *
+     * @param clazz                  Class to model.
+     * @param customization          Customization of the class parsed from annotations.
+     * @param parentClassModel       Class model of parent class.
+     * @param propertyNamingStrategy Property naming strategy.
+     */
+    public ClassModel(Class<?> clazz,
+                      ClassCustomization customization,
+                      ClassModel parentClassModel,
+                      PropertyNamingStrategy propertyNamingStrategy) {
+        this.clazz = clazz;
+        this.classCustomization = customization;
+        this.parentClassModel = parentClassModel;
+        this.propertyNamingStrategy = propertyNamingStrategy;
+        setProperties(new ArrayList<>());
+    }
+
+    /**
+     * Search for field in this class model and superclasses of its class.
+     *
+     * @param jsonReadName name as it appears in JSON during reading.
+     * @return PropertyModel if found.
+     */
+    public PropertyModel findPropertyModelByJsonReadName(String jsonReadName) {
+        Objects.requireNonNull(jsonReadName);
+        return searchProperty(this, jsonReadName);
+    }
+
+    private PropertyModel searchProperty(ClassModel classModel, String jsonReadName) {
+        //Standard javabean properties without overridden name (most of the cases)
+        final PropertyModel result = classModel.getPropertyModel(jsonReadName);
+        if (result != null && result.getPropertyName().equals(result.getReadName())) {
+            return result;
+        }
+        //Search for overridden name on setter with @JsonbProperty annotation
+        for (PropertyModel propertyModel : properties.values()) {
+            if (equalsReadName(jsonReadName, propertyModel)) {
+                return propertyModel;
+            }
+        }
+        //property not found
+        return null;
+    }
+
+    /**
+     * Check if name is equal according to property strategy.
+     * In case of {@link StrategiesProvider#CASE_INSENSITIVE_STRATEGY} ignore case.
+     * User can provide own strategy implementation, cast to custom interface is not an option.
+     *
+     * @return True if names are equal.
+     */
+    private boolean equalsReadName(String jsonName, PropertyModel propertyModel) {
+        final String propertyReadName = propertyModel.getReadName();
+        if (propertyNamingStrategy == StrategiesProvider.CASE_INSENSITIVE_STRATEGY) {
+            return jsonName.equalsIgnoreCase(propertyReadName);
+        }
+        return jsonName.equals(propertyReadName);
+    }
+
+    /**
+     * Gets type.
+     *
+     * @return Type.
+     */
+    public Class<?> getType() {
+        return clazz;
+    }
+
+    /**
+     * Introspected customization for a class.
+     *
+     * @return Immutable class customization.
+     */
+    public ClassCustomization getClassCustomization() {
+        return classCustomization;
+    }
+
+    /**
+     * Class model of parent class if present.
+     *
+     * @return class model of a parent
+     */
+    public ClassModel getParentClassModel() {
+        return parentClassModel;
+    }
+
+    /**
+     * Get sorted class properties copy, combination of field and its getter / setter, javabeans alike.
+     *
+     * @return sorted class properties.
+     */
+    public PropertyModel[] getSortedProperties() {
+        return sortedProperties;
+    }
+
+    /**
+     * Sets parsed properties of the class.
+     *
+     * @param parsedProperties class properties
+     */
+    public void setProperties(List<PropertyModel> parsedProperties) {
+        sortedProperties = parsedProperties.toArray(new PropertyModel[] {});
+        this.properties = parsedProperties.stream().collect(Collectors.toMap(PropertyModel::getPropertyName, (mod) -> mod));
+    }
+
+    /**
+     * Get class properties copy, combination of field and its getter / setter, javabeans alike.
+     *
+     * @return class properties.
+     */
+    public Map<String, PropertyModel> getProperties() {
+        return Collections.unmodifiableMap(properties);
+    }
+
+    /**
+     * Default no argument constructor of the class used for deserialization.
+     *
+     * @return default constructor
+     */
+    public Constructor<?> getDefaultConstructor() {
+        // Lazy-loads the default constructor to avoid Java 9+ "Illegal reflective access" warnings where possible.
+        // Example: Deserialization into Map won't use this constructor, and therefore never needs to call this method.
+        // Note: Null is a valid result and needs to be cached.
+        if (!isInitialized.get()) {
+            if (ClassMultiReleaseExtension.isRecord(clazz)) {
+                //No default constructor should be used in case of records
+                defaultConstructor = null;
+            } else {
+                defaultConstructor = ReflectionUtils.getDefaultConstructor(clazz, false);
+            }
+            isInitialized.set(true);
+        }
+        return defaultConstructor;
+    }
+
+    @Override
+    public String toString() {
+        return "ClassModel{"
+                + "clazz=" + clazz
+                + '}';
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/CreatorModel.java b/src/main/java/org/eclipse/yasson/internal/model/CreatorModel.java
new file mode 100644
index 0000000..3ab3d90
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/CreatorModel.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Type;
+
+import org.eclipse.yasson.internal.AnnotationIntrospector;
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.components.DeserializerBinding;
+import org.eclipse.yasson.internal.model.customization.CreatorCustomization;
+
+/**
+ * Parameter for creator constructor / method model.
+ */
+public class CreatorModel {
+
+    private final String name;
+
+    private final Type type;
+
+    private final CreatorCustomization creatorCustomization;
+
+    /**
+     * Creates a new instance.
+     *  @param name      Parameter name
+     * @param parameter constructor parameter
+     * @param executable creator executable
+     * @param context   jsonb context
+     */
+    public CreatorModel(String name, Parameter parameter, Executable executable, JsonbContext context) {
+        this.name = name;
+        this.type = parameter.getParameterizedType();
+
+        AnnotationIntrospector annotationIntrospector = context.getAnnotationIntrospector();
+
+        JsonbAnnotatedElement<Parameter> annotated = new JsonbAnnotatedElement<>(parameter);
+        boolean required = context.getAnnotationIntrospector().requiredParameters(executable, annotated);
+        JsonbNumberFormatter constructorNumberFormatter = context.getAnnotationIntrospector()
+                .getConstructorNumberFormatter(annotated);
+        JsonbDateFormatter constructorDateFormatter = context.getAnnotationIntrospector().getConstructorDateFormatter(annotated);
+        DeserializerBinding<?> deserializerBinding = annotationIntrospector.getDeserializerBinding(parameter);
+        AdapterBinding adapterBinding = annotationIntrospector.getAdapterBinding(parameter);
+        final JsonbAnnotatedElement<Class<?>> clsElement = annotationIntrospector.collectAnnotations(parameter.getType());
+        deserializerBinding = deserializerBinding == null
+                ? annotationIntrospector.getDeserializerBinding(clsElement)
+                : deserializerBinding;
+        adapterBinding = adapterBinding == null
+                ? annotationIntrospector.getAdapterBinding(clsElement)
+                : adapterBinding;
+        this.creatorCustomization = CreatorCustomization.builder()
+                .adapterBinding(adapterBinding)
+                .deserializerBinding(deserializerBinding)
+                .serializerBinding(annotationIntrospector.getSerializerBinding(clsElement))
+                .numberFormatter(constructorNumberFormatter)
+                .dateFormatter(constructorDateFormatter)
+                .required(required)
+                .build();
+    }
+
+    /**
+     * Gets parameter name.
+     *
+     * @return Parameter name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    public CreatorCustomization getCustomization() {
+        return creatorCustomization;
+    }
+
+    /**
+     * Gets parameter type.
+     *
+     * @return Parameter type.
+     */
+    public Type getType() {
+        return type;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotatedElement.java b/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotatedElement.java
new file mode 100644
index 0000000..b93abe9
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotatedElement.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Annotation holder for classes, superclasses, interfaces, fields, getters and setters.
+ *
+ * @param <T> annotated element
+ */
+public class JsonbAnnotatedElement<T extends AnnotatedElement> {
+
+    private final Map<Class<? extends Annotation>, LinkedList<AnnotationWrapper<?>>> annotations = new HashMap<>(4);
+
+    private final T element;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param element Element.
+     */
+    public JsonbAnnotatedElement(T element) {
+        for (Annotation ann : element.getAnnotations()) {
+            if (element instanceof Class) {
+                putAnnotation(ann, false, (Class<?>) element);
+            } else {
+                putAnnotation(ann, false, null);
+            }
+        }
+
+        this.element = element;
+    }
+
+    /**
+     * Gets element.
+     *
+     * @return Element.
+     */
+    public T getElement() {
+        return element;
+    }
+
+    /**
+     * Get an annotation by type.
+     *
+     * @param <AT>            Type of annotation
+     * @param annotationClass Type of annotation
+     * @return Annotation by passed type
+     */
+    public <AT extends Annotation> Optional<AT> getAnnotation(Class<AT> annotationClass) {
+        return Optional.ofNullable(annotations.get(annotationClass))
+                .map(LinkedList::getFirst)
+                .map(AnnotationWrapper::getAnnotation)
+                .map(annotationClass::cast);
+    }
+
+    public <AT extends Annotation> LinkedList<AnnotationWrapper<?>> getAnnotations(Class<AT> annotationClass) {
+        return annotations.getOrDefault(annotationClass, new LinkedList<>());
+    }
+
+    @SuppressWarnings("unchecked")
+    public <AT extends Annotation> AnnotationWrapper<AT> getAnnotationWrapper(Class<AT> annotationClass) {
+        return (AnnotationWrapper<AT>) annotations.get(annotationClass).getFirst();
+    }
+
+    public Annotation[] getAnnotations() {
+        return annotations.values().stream()
+                .flatMap(Collection::stream)
+                .map(AnnotationWrapper::getAnnotation)
+                .toArray(Annotation[]::new);
+    }
+
+    /**
+     * Adds annotation.
+     *
+     * @param annotation Annotation to add.
+     * @param definedType
+     */
+    public void putAnnotation(Annotation annotation, boolean inherited, Class<?> definedType) {
+//        if (annotations.containsKey(annotation.annotationType())) {
+//            throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR,
+//                                                         "Annotation already present: " + annotation));
+//        }
+//        annotations.put(annotation.annotationType(), new AnnotationWrapper(annotation, inherited));
+        annotations.computeIfAbsent(annotation.annotationType(), aClass -> new LinkedList<>())
+                        .add(new AnnotationWrapper<Annotation>(annotation, inherited, definedType));
+    }
+
+    public void putAnnotationWrapper(AnnotationWrapper<?> annotationWrapper) {
+        annotations.computeIfAbsent(annotationWrapper.getAnnotation().annotationType(), aClass -> new LinkedList<>())
+                .add(annotationWrapper);
+    }
+
+    public static final class AnnotationWrapper<T extends Annotation> {
+
+        private final T annotation;
+        private final boolean inherited;
+        private final Class<?> definedType;
+
+        public AnnotationWrapper(T annotation, boolean inherited, Class<?> definedType) {
+            this.annotation = annotation;
+            this.inherited = inherited;
+            this.definedType = definedType;
+        }
+
+        public T getAnnotation() {
+            return annotation;
+        }
+
+        public boolean isInherited() {
+            return inherited;
+        }
+
+        public Class<?> getDefinedType() {
+            return definedType;
+        }
+
+        @Override
+        public String toString() {
+            return definedType.getName();
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/JsonbCreator.java b/src/main/java/org/eclipse/yasson/internal/model/JsonbCreator.java
new file mode 100644
index 0000000..9036928
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/JsonbCreator.java
@@ -0,0 +1,99 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Object holding reference to Constructor / Method for custom object creation.
+ */
+public class JsonbCreator {
+
+    private final Executable executable;
+
+    private final CreatorModel[] params;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param executable    Executable.
+     * @param creatorModels Parameters.
+     */
+    public JsonbCreator(Executable executable, CreatorModel[] creatorModels) {
+        this.executable = executable;
+        this.params = creatorModels;
+    }
+
+    /**
+     * Create instance by either constructor or factory method, with provided parameter values and a Class to call on.
+     *
+     * @param params parameters to be passed into constructor / factory method
+     * @param on     class to call onto
+     * @param <T>    Type of class / instance
+     * @return instance
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T call(Object[] params, Class<T> on) {
+        try {
+            if (executable instanceof Constructor) {
+                return ((Constructor<T>) executable).newInstance(params);
+            } else {
+                return (T) ((Method) executable).invoke(on, params);
+            }
+        } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.ERROR_CALLING_JSONB_CREATOR, on), e);
+        }
+    }
+
+    /**
+     * True if param name is one of creator params.
+     *
+     * @param paramName Param name to check.
+     * @return True if found.
+     */
+    public boolean contains(String paramName) {
+        return findByName(paramName) != null;
+    }
+
+    /**
+     * Find creator parameter by name.
+     *
+     * @param paramName parameter name as it appear in json document.
+     * @return Creator parameter.
+     */
+    public CreatorModel findByName(String paramName) {
+        for (CreatorModel param : params) {
+            if (param.getName().equals(paramName)) {
+                return param;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Parameters of this creator.
+     *
+     * @return Parameters.
+     */
+    public CreatorModel[] getParams() {
+        return params;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/ModulesUtil.java b/src/main/java/org/eclipse/yasson/internal/model/ModulesUtil.java
new file mode 100644
index 0000000..2f27139
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/ModulesUtil.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.lang.invoke.MethodHandles;
+
+class ModulesUtil {
+
+    private ModulesUtil() {
+    }
+
+    static MethodHandles.Lookup lookup(){
+        return MethodHandles.publicLookup();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/Property.java b/src/main/java/org/eclipse/yasson/internal/model/Property.java
new file mode 100644
index 0000000..0fa99e0
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/Property.java
@@ -0,0 +1,183 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+import jakarta.json.bind.JsonbException;
+
+/**
+ * Property of a class, field, getter and setter methods (javabean alike).
+ * Used during class model initialization, than dereferenced.
+ */
+public class Property {
+
+    private final String name;
+
+    private final JsonbAnnotatedElement<Class<?>> declaringClassElement;
+
+    private JsonbAnnotatedElement<Field> fieldElement;
+
+    private JsonbAnnotatedElement<Method> getterElement;
+
+    private JsonbAnnotatedElement<Method> setterElement;
+
+    /**
+     * Create instance of property.
+     *
+     * @param name                not null
+     * @param declaringClassModel Class model for a class declaring property.
+     */
+    public Property(String name, JsonbAnnotatedElement<Class<?>> declaringClassModel) {
+        this.name = name;
+        this.declaringClassElement = declaringClassModel;
+    }
+
+    /**
+     * Name of a property, java bean convention.
+     *
+     * @return name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * {@link Field} representing property if any.
+     *
+     * @return field if present
+     */
+    public Field getField() {
+        if (fieldElement == null) {
+            return null;
+        }
+        return fieldElement.getElement();
+    }
+
+    /**
+     * @param field field not null
+     */
+    public void setField(Field field) {
+        this.fieldElement = new JsonbAnnotatedElement<>(field);
+    }
+
+    /**
+     * {@link Method} representing getter of a property if any.
+     *
+     * @return getter if present
+     */
+    public Method getGetter() {
+        if (getterElement == null) {
+            return null;
+        }
+        return getterElement.getElement();
+    }
+
+    /**
+     * @param getter not null
+     */
+    public void setGetter(Method getter) {
+        this.getterElement = new JsonbAnnotatedElement<>(getter);
+    }
+
+    /**
+     * {@link Method} representing setter of a property if any.
+     *
+     * @return setter if present
+     */
+    public Method getSetter() {
+        if (setterElement == null) {
+            return null;
+        }
+        return setterElement.getElement();
+    }
+
+    /**
+     * @param setter setter not null
+     */
+    public void setSetter(Method setter) {
+        this.setterElement = new JsonbAnnotatedElement<>(setter);
+    }
+
+    /**
+     * Class element with annotation under construction for declaring class of this property.
+     * This ClassModel is not fully initialized yet.
+     *
+     * @return ClassModel
+     */
+    public JsonbAnnotatedElement<Class<?>> getDeclaringClassElement() {
+        return declaringClassElement;
+    }
+
+    /**
+     * Extracts type from first not null element:
+     * Field, Getter, Setter.
+     *
+     * @return type of a property
+     */
+    public Type getPropertyType() {
+        if (getField() != null) {
+            return getField().getGenericType();
+        } else if (getGetter() != null) {
+            return getGetterType();
+        } else if (getSetter() != null) {
+            return getSetterType();
+        }
+        throw new JsonbException("Empty property: " + name);
+    }
+
+    Type getGetterType() {
+        if (getGetter() != null) {
+            return getGetter().getGenericReturnType();
+        }
+        return null;
+    }
+
+    Type getSetterType() {
+        Type[] genericParameterTypes = getSetter().getGenericParameterTypes();
+        if (genericParameterTypes.length != 1) {
+            throw new JsonbException("Invalid count of arguments for setter: " + getSetter());
+        }
+        return genericParameterTypes[0];
+    }
+
+    /**
+     * Element with field and its annotations.
+     *
+     * @return field with annotations
+     */
+    public JsonbAnnotatedElement<Field> getFieldElement() {
+        return fieldElement;
+    }
+
+    /**
+     * Element with getter and its annotations.
+     *
+     * @return getter with annotations
+     */
+    public JsonbAnnotatedElement<Method> getGetterElement() {
+        return getterElement;
+    }
+
+    /**
+     * Element with setter and its annotations.
+     *
+     * @return setter with annotations
+     */
+    public JsonbAnnotatedElement<Method> getSetterElement() {
+        return setterElement;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/PropertyModel.java b/src/main/java/org/eclipse/yasson/internal/model/PropertyModel.java
new file mode 100644
index 0000000..707b03a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/PropertyModel.java
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+
+import org.eclipse.yasson.internal.AnnotationIntrospector;
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.components.SerializerBinding;
+import org.eclipse.yasson.internal.model.customization.PropertyCustomization;
+
+/**
+ * A model for class property.
+ * Property is JavaBean alike meta information field / getter / setter of a property in class.
+ */
+public final class PropertyModel implements Comparable<PropertyModel> {
+
+    private static final MethodHandles.Lookup LOOKUP = ModulesUtil.lookup();
+
+    /**
+     * Field propertyName as in class by java bean convention.
+     */
+    private final String propertyName;
+
+    /**
+     * Calculated name to be used when reading json document.
+     */
+    private final String readName;
+
+    /**
+     * Calculated name to be used when writing json document.
+     */
+    private final String writeName;
+
+    /**
+     * Field propertyType.
+     */
+    private final Type propertyType;
+
+    /**
+     * Model of the class this field belongs to.
+     */
+    private final ClassModel classModel;
+
+    private final Property property;
+
+    /**
+     * Customization of this property.
+     */
+    private final PropertyCustomization customization;
+
+    private final MethodHandle getValueHandle;
+
+    private final MethodHandle setValueHandle;
+
+    private final Field field;
+
+    private final Method getter;
+
+    private final Method setter;
+
+    private final Type getterMethodType;
+
+    private final Type setterMethodType;
+
+    /**
+     * Create a new PropertyModel that merges two existing PropertyModel that have identical read/write names.
+     * The input PropertyModel objects MUST be equal (a.equals(b) == true)
+     *
+     * @param a a PropertyModel instance to merge
+     * @param b the other PropertyModel instance to merge
+     */
+    public PropertyModel(PropertyModel a, PropertyModel b) {
+        if (!a.equals(b)) {
+            throw new IllegalStateException("Property models " + a + " and " + b + " cannot be merged");
+        }
+
+        // Initial cloning steps
+        this.classModel = a.classModel;
+        this.propertyName = a.propertyName;
+        this.readName = a.readName;
+        this.writeName = a.writeName;
+        this.propertyType = a.propertyType;
+        this.customization = a.customization;
+
+        // Merging steps
+        this.getterMethodType = a.getterMethodType != null ? a.getterMethodType : b.getterMethodType;
+        this.setterMethodType = a.setterMethodType != null ? a.setterMethodType : b.setterMethodType;
+        this.property = a.property;
+        if (b.property.getField() != null) {
+            this.property.setField(b.property.getField());
+        }
+        if (b.property.getGetter() != null) {
+            this.property.setGetter(b.property.getGetter());
+        }
+        if (b.property.getSetter() != null) {
+            this.property.setSetter(b.property.getSetter());
+        }
+        this.field = property.getField();
+        this.getter = property.getGetter();
+        this.setter = property.getSetter();
+
+        PropertyVisibilityStrategy strategy = classModel.getClassCustomization().getPropertyVisibilityStrategy();
+        this.getValueHandle = createReadHandle(field, getter, isMethodVisible(getter, strategy), strategy);
+        this.setValueHandle = createWriteHandle(field, setter, isMethodVisible(setter, strategy), strategy);
+    }
+
+    /**
+     * Creates an instance.
+     *
+     * @param classModel   Class model of declaring class.
+     * @param property     Property.
+     * @param jsonbContext Context.
+     */
+    public PropertyModel(ClassModel classModel, Property property, JsonbContext jsonbContext) {
+        this.classModel = classModel;
+        this.property = property;
+        this.propertyName = property.getName();
+        this.propertyType = property.getPropertyType();
+        this.field = property.getField();
+        this.getter = property.getGetter();
+        this.setter = property.getSetter();
+
+        PropertyVisibilityStrategy strategy = classModel.getClassCustomization().getPropertyVisibilityStrategy();
+        boolean getterVisible = isMethodVisible(getter, strategy);
+        boolean setterVisible = isMethodVisible(setter, strategy);
+
+        this.getValueHandle = createReadHandle(field, getter, getterVisible, strategy);
+        this.setValueHandle = createWriteHandle(field, setter, setterVisible, strategy);
+        this.getterMethodType = getterVisible ? property.getGetterType() : null;
+        this.setterMethodType = setterVisible ? property.getSetterType() : null;
+        this.customization = introspectCustomization(property, jsonbContext, classModel);
+        this.readName = calculateReadWriteName(customization.getJsonReadName(), propertyName,
+                                               jsonbContext.getConfigProperties().getPropertyNamingStrategy());
+        this.writeName = calculateReadWriteName(customization.getJsonWriteName(), propertyName,
+                                                jsonbContext.getConfigProperties().getPropertyNamingStrategy());
+    }
+
+    /**
+     * Returns which type should be used to deserialization.
+     *
+     * @return deserialization type
+     */
+    public Type getPropertyDeserializationType() {
+        return setterMethodType == null ? propertyType : setterMethodType;
+    }
+
+    /**
+     * Returns which type should be used to serialization.
+     *
+     * @return serialization type
+     */
+    public Type getPropertySerializationType() {
+        return getterMethodType == null ? propertyType : getterMethodType;
+    }
+
+    private SerializerBinding<?> getUserSerializerBinding(Property property, JsonbContext jsonbContext) {
+        final SerializerBinding<?> serializerBinding = jsonbContext.getAnnotationIntrospector().getSerializerBinding(property);
+        if (serializerBinding != null) {
+            return serializerBinding;
+        }
+        return jsonbContext.getComponentMatcher().getSerializerBinding(getPropertySerializationType(), null).orElse(null);
+    }
+
+    private PropertyCustomization introspectCustomization(Property property, JsonbContext jsonbContext, ClassModel classModel) {
+        final AnnotationIntrospector introspector = jsonbContext.getAnnotationIntrospector();
+        final PropertyCustomization.Builder builder = PropertyCustomization.builder();
+        //drop all other annotations for transient properties
+        EnumSet<AnnotationTarget> transientInfo = introspector.getJsonbTransientCategorized(property);
+        ClassModel parent = classModel;
+        // Check parent classes for transient annotations
+        while ((parent = parent.getParentClassModel()) != null) {
+            PropertyModel parentProperty = parent.getPropertyModel(property.getName());
+            if (parentProperty != null) {
+                if (parentProperty.customization.isReadTransient()) {
+                    transientInfo.add(AnnotationTarget.GETTER);
+                }
+                if (parentProperty.customization.isWriteTransient()) {
+                    transientInfo.add(AnnotationTarget.SETTER);
+                }
+            }
+        }
+        if (transientInfo.size() != 0) {
+            builder.readTransient(transientInfo.contains(AnnotationTarget.GETTER));
+            builder.writeTransient(transientInfo.contains(AnnotationTarget.SETTER));
+
+            if (transientInfo.contains(AnnotationTarget.PROPERTY)) {
+                if (!transientInfo.contains(AnnotationTarget.GETTER)) {
+                    builder.readTransient(true);
+                }
+                if (!transientInfo.contains(AnnotationTarget.SETTER)) {
+                    builder.writeTransient(true);
+                }
+            }
+
+            if (builder.readTransient()) {
+                introspector.checkTransientIncompatible(property.getFieldElement());
+                introspector.checkTransientIncompatible(property.getGetterElement());
+            }
+            if (builder.writeTransient()) {
+                introspector.checkTransientIncompatible(property.getFieldElement());
+                introspector.checkTransientIncompatible(property.getSetterElement());
+            }
+        }
+
+        if (!builder.readTransient()) {
+            builder.jsonWriteName(introspector.getJsonbPropertyJsonWriteName(property));
+            builder.nillable(introspector.isPropertyNillable(property).orElse(classModel.getClassCustomization().isNillable()));
+            builder.serializerBinding(getUserSerializerBinding(property, jsonbContext));
+        }
+
+        if (!builder.writeTransient()) {
+            builder.jsonReadName(introspector.getJsonbPropertyJsonReadName(property));
+            builder.deserializerBinding(introspector.getDeserializerBinding(property));
+        }
+
+        final AdapterBinding adapterBinding = jsonbContext.getAnnotationIntrospector().getAdapterBinding(property);
+        if (adapterBinding != null) {
+            builder.serializeAdapter(adapterBinding);
+            builder.deserializeAdapter(adapterBinding);
+        } else {
+            builder.serializeAdapter(jsonbContext.getComponentMatcher()
+                                             .getSerializeAdapterBinding(getPropertySerializationType(), null).orElse(null));
+            builder.deserializeAdapter(jsonbContext.getComponentMatcher()
+                                               .getDeserializeAdapterBinding(getPropertyDeserializationType(), null)
+                                               .orElse(null));
+        }
+
+        introspectDateFormatter(property, introspector, builder, jsonbContext);
+        introspectNumberFormatter(property, introspector, builder);
+        builder.implementationClass(introspector.getImplementationClass(property));
+
+        return builder.build();
+    }
+
+    private static void introspectDateFormatter(Property property,
+                                                AnnotationIntrospector introspector,
+                                                PropertyCustomization.Builder builder,
+                                                JsonbContext jsonbContext) {
+        /*
+         * If @JsonbDateFormat is placed on getter implementation must use this format on serialization.
+         * If @JsonbDateFormat is placed on setter implementation must use this format on deserialization.
+         * If @JsonbDateFormat is placed on field implementation must use this format on serialization and deserialization.
+         *
+         * Priority from high to low is getter / setter > field > class > package > global configuration
+         */
+        Map<AnnotationTarget, JsonbDateFormatter> jsonDateFormatCategorized = introspector
+                .getJsonbDateFormatCategorized(property);
+        final JsonbDateFormatter configDateFormatter = jsonbContext.getConfigProperties().getConfigDateFormatter();
+
+        if (!builder.readTransient()) {
+            final JsonbDateFormatter dateFormatter = getTargetForMostPreciseScope(jsonDateFormatCategorized,
+                                                                                  AnnotationTarget.GETTER,
+                                                                                  AnnotationTarget.PROPERTY,
+                                                                                  AnnotationTarget.CLASS);
+
+            builder.serializeDateFormatter(dateFormatter != null ? dateFormatter : configDateFormatter);
+        }
+
+        if (!builder.writeTransient()) {
+            final JsonbDateFormatter dateFormatter = getTargetForMostPreciseScope(jsonDateFormatCategorized,
+                                                                                  AnnotationTarget.SETTER,
+                                                                                  AnnotationTarget.PROPERTY,
+                                                                                  AnnotationTarget.CLASS);
+
+            builder.deserializeDateFormatter(dateFormatter != null ? dateFormatter : configDateFormatter);
+        }
+    }
+
+    private static void introspectNumberFormatter(Property property,
+                                                  AnnotationIntrospector introspector,
+                                                  PropertyCustomization.Builder builder) {
+        /*
+         * If @JsonbNumberFormat is placed on getter implementation must use this format on serialization.
+         * If @JsonbNumberFormat is placed on setter implementation must use this format on deserialization.
+         * If @JsonbNumberFormat is placed on field implementation must use this format on serialization and deserialization.
+         *
+         * Priority from high to low is getter / setter > field > class > package > global configuration
+         */
+        Map<AnnotationTarget, JsonbNumberFormatter> jsonNumberFormatCategorized = introspector.getJsonNumberFormatter(property);
+
+        if (!builder.readTransient()) {
+            builder.serializeNumberFormatter(getTargetForMostPreciseScope(jsonNumberFormatCategorized,
+                                                                          AnnotationTarget.GETTER,
+                                                                          AnnotationTarget.PROPERTY,
+                                                                          AnnotationTarget.CLASS));
+        }
+
+        if (!builder.writeTransient()) {
+            builder.deserializeNumberFormatter(getTargetForMostPreciseScope(jsonNumberFormatCategorized,
+                                                                            AnnotationTarget.SETTER,
+                                                                            AnnotationTarget.PROPERTY,
+                                                                            AnnotationTarget.CLASS));
+        }
+    }
+
+    /**
+     * Pull result for most significant scope defined by order of annotation targets.
+     *
+     * @param collectedAnnotations all targets
+     * @param targets              ordered target types by scope
+     */
+    private static <T> T getTargetForMostPreciseScope(Map<AnnotationTarget, T> collectedAnnotations,
+                                                      AnnotationTarget... targets) {
+        for (AnnotationTarget target : targets) {
+            final T result = collectedAnnotations.get(target);
+            if (result != null) {
+                return result;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Gets property's value.
+     *
+     * @param object object to read property from
+     * @return property's value
+     */
+    public Object getValue(Object object) {
+        try {
+            return getValueHandle.invoke(object);
+        } catch (Throwable e) {
+            throw new JsonbException("Error getting value on: " + object, e);
+        }
+    }
+
+    /**
+     * Sets a property.
+     *
+     * If not writable (final, transient, static), ignores property.
+     *
+     * @param object Object to set value in.
+     * @param value  Value to set.
+     */
+    public void setValue(Object object, Object value) {
+        if (!isWritable()) {
+            return;
+        }
+        try {
+            setValueHandle.invoke(object, value);
+        } catch (Throwable e) {
+            throw new JsonbException("Error setting value on: " + object, e);
+        }
+    }
+
+    /**
+     * Property is readable. Based on access policy and java field modifiers.
+     *
+     * @return true if can be serialized to JSON
+     */
+    public boolean isReadable() {
+        return !customization.isReadTransient() && this.getValueHandle != null;
+    }
+
+    /**
+     * Property is writable. Based on access policy and java field modifiers.
+     *
+     * @return true if can be deserialized from JSON
+     */
+    public boolean isWritable() {
+        return !customization.isWriteTransient() && this.setValueHandle != null;
+    }
+
+    /**
+     * Default property name according to Field / Getter / Setter method names.
+     * This name is use for identifying properties, for JSON serialization is used customized name
+     * which may be derived from default name.
+     *
+     * @return default name
+     */
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    /**
+     * Model of declaring class of this property.
+     *
+     * @return class model
+     */
+    public ClassModel getClassModel() {
+        return classModel;
+    }
+
+    /**
+     * Introspected customization of a property.
+     *
+     * @return immutable property customization
+     */
+    public PropertyCustomization getCustomization() {
+        return customization;
+    }
+
+    @Override
+    public int compareTo(PropertyModel o) {
+        int compare = readName.compareTo(o.readName);
+        return compare == 0 ? writeName.compareTo(o.writeName) : compare;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PropertyModel other = (PropertyModel) o;
+        return Objects.equals(readName, other.readName)
+                && Objects.equals(writeName, other.writeName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(readName, writeName);
+    }
+
+    /**
+     * Gets a name of JSON document property to read this property from.
+     *
+     * @return Name of JSON document property.
+     */
+    public String getReadName() {
+        return readName;
+    }
+
+    public String getWriteName() {
+        return writeName;
+    }
+
+    /**
+     * If customized by JsonbPropertyAnnotation, than is used, otherwise use strategy to translate.
+     * Since this is cached for performance reasons strategy has to be consistent
+     * with calculated values for same input.
+     */
+    private static String calculateReadWriteName(String readWriteName, String propertyName, PropertyNamingStrategy strategy) {
+        return readWriteName != null ? readWriteName : strategy.translateName(propertyName);
+    }
+
+    /**
+     * Field of a javabean property.
+     *
+     * @return {@link Field field}
+     */
+    public Field getField() {
+        return field;
+    }
+
+    /**
+     * Setter of a javabean property.
+     *
+     * @return {@link Method getter}
+     */
+    public Method getGetter() {
+        return getter;
+    }
+
+    /**
+     * Getter of a javabean property.
+     *
+     * @return {@link Method setter}
+     */
+    public Method getSetter() {
+        return setter;
+    }
+
+    // Used in ClassParser
+    public static boolean isPropertyReadable(Field field, Method getter, PropertyVisibilityStrategy strategy) {
+        return createReadHandle(field, getter, isMethodVisible(getter, strategy), strategy) != null;
+    }
+
+    private static MethodHandle createReadHandle(Field field,
+                                                 Method getter,
+                                                 boolean getterVisible,
+                                                 PropertyVisibilityStrategy strategy) {
+        boolean fieldReadable = field == null || (field.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC)) == 0;
+
+        if (fieldReadable) {
+            if (getter != null && getterVisible) {
+                try {
+                    return LOOKUP.unreflect(getter);
+                } catch (Throwable e) {
+                    throw new JsonbException("Error accessing getter '" + getter.getName() + "' declared in '" + getter
+                            .getDeclaringClass() + "'", e);
+                }
+            }
+            if (isFieldVisible(field, getter, strategy)) {
+                try {
+                    return LOOKUP.unreflectGetter(field);
+                } catch (IllegalAccessException e) {
+                    throw new JsonbException("Error accessing field '" + field.getName() + "' declared in '" + field
+                            .getDeclaringClass() + "'", e);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private static MethodHandle createWriteHandle(Field field,
+                                                  Method setter,
+                                                  boolean setterVisible,
+                                                  PropertyVisibilityStrategy strategy) {
+        boolean fieldWritable =
+                field == null || (field.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC | Modifier.FINAL)) == 0;
+
+        if (fieldWritable) {
+            if (setter != null && setterVisible && !setter.getDeclaringClass().isAnonymousClass()) {
+                try {
+                    return LOOKUP.unreflect(setter);
+                } catch (IllegalAccessException e) {
+                    throw new JsonbException("Error accessing setter '" + setter.getName() + "' declared in '" + setter
+                            .getDeclaringClass() + "'", e);
+                }
+            }
+            if (isFieldVisible(field, setter, strategy) && !field.getDeclaringClass().isAnonymousClass()) {
+                try {
+                    return LOOKUP.unreflectSetter(field);
+                } catch (IllegalAccessException e) {
+                    throw new JsonbException("Error accessing field '" + field.getName() + "' declared in '" + field
+                            .getDeclaringClass() + "'", e);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private static boolean isFieldVisible(Field field, Method method, PropertyVisibilityStrategy strategy) {
+        if (field == null) {
+            return false;
+        }
+        boolean accessible = isVisible(strat -> strat.isVisible(field), method, strategy);
+        //overridden by strategy, or anonymous class (readable by spec)
+        if (accessible && (
+                !Modifier.isPublic(field.getModifiers())
+                        || field.getDeclaringClass().isAnonymousClass()
+                        || isNotPublicAndNonNested(field.getDeclaringClass()))) {
+            overrideAccessible(field);
+        }
+        return accessible;
+    }
+
+    private static boolean isNotPublicAndNonNested(Class<?> declaringClass) {
+        return !declaringClass.isMemberClass() && !Modifier.isPublic(declaringClass.getModifiers());
+    }
+
+    private static boolean isMethodVisible(Method method, PropertyVisibilityStrategy strategy) {
+        if (method == null || Modifier.isStatic(method.getModifiers())) {
+            return false;
+        }
+
+        boolean accessible = isVisible(strat -> strat.isVisible(method), method, strategy);
+        //overridden by strategy, anonymous class, or lambda
+        if (accessible && (
+                !Modifier.isPublic(method.getModifiers()) || method.getDeclaringClass().isAnonymousClass() || method
+                        .getDeclaringClass().isSynthetic())) {
+            overrideAccessible(method);
+        }
+        return accessible;
+    }
+
+    private static void overrideAccessible(AccessibleObject accessibleObject) {
+        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+            accessibleObject.setAccessible(true);
+            return null;
+        });
+    }
+
+    /**
+     * Look up class and package level @JsonbVisibility, or global config PropertyVisibilityStrategy.
+     * If any is found it is used for resolving visibility by calling provided visibilityCheckFunction.
+     *
+     * @param visibilityCheckFunction function declaring visibility check
+     * @return Optional with result of visibility check, or empty optional if no strategy is found
+     */
+    private static boolean isVisible(Predicate<PropertyVisibilityStrategy> visibilityCheckFunction,
+                                     Method method,
+                                     PropertyVisibilityStrategy strategy) {
+        return strategy != null
+                ? visibilityCheckFunction.test(strategy)
+                : visibilityCheckFunction.test(new DefaultVisibilityStrategy(method));
+    }
+
+    private static final class DefaultVisibilityStrategy implements PropertyVisibilityStrategy {
+
+        private final Method method;
+
+        DefaultVisibilityStrategy(Method method) {
+            this.method = method;
+        }
+
+        @Override
+        public boolean isVisible(Field field) {
+            //don't check field if getter is not visible (forced by spec)
+            return (method == null || isVisible(method)) && Modifier.isPublic(field.getModifiers());
+        }
+
+        @Override
+        public boolean isVisible(Method method) {
+            return Modifier.isPublic(method.getModifiers());
+        }
+    }
+
+    public MethodHandle getGetValueHandle() {
+        return getValueHandle;
+    }
+
+    public MethodHandle getSetValueHandle() {
+        return setValueHandle;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/ReverseTreeMap.java b/src/main/java/org/eclipse/yasson/internal/model/ReverseTreeMap.java
new file mode 100644
index 0000000..e6608e9
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/ReverseTreeMap.java
@@ -0,0 +1,32 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.util.Comparator;
+import java.util.TreeMap;
+
+/**
+ * TreeMap with a reverse ordering by default.
+ *
+ * @param <K> comparable key
+ * @param <V> value
+ */
+public class ReverseTreeMap<K extends Comparable<? super K>, V> extends TreeMap<K, V> {
+
+    /**
+     * Default constructor of a TreeMap with reverse order.
+     */
+    public ReverseTreeMap() {
+        super(Comparator.reverseOrder());
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/ClassCustomization.java b/src/main/java/org/eclipse/yasson/internal/model/customization/ClassCustomization.java
new file mode 100644
index 0000000..f53a0ec
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/ClassCustomization.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+
+/**
+ * Customization which could be applied on a class or package level.
+ */
+public class ClassCustomization extends CustomizationBase {
+
+    private static final ClassCustomization EMPTY = new ClassCustomization(new Builder());
+
+    private final JsonbCreator creator;
+    private final String[] propertyOrder;
+    private final JsonbNumberFormatter numberFormatter;
+    private final JsonbDateFormatter dateTimeFormatter;
+    private final PropertyVisibilityStrategy propertyVisibilityStrategy;
+    private final TypeInheritanceConfiguration typeInheritanceConfiguration;
+
+    /**
+     * Copies properties from builder an creates immutable instance.
+     *
+     * @param builder not null
+     */
+    private ClassCustomization(Builder builder) {
+        super(builder);
+        this.creator = builder.creator;
+        this.propertyOrder = builder.propertyOrder;
+        this.numberFormatter = builder.numberFormatter;
+        this.dateTimeFormatter = builder.dateTimeFormatter;
+        this.propertyVisibilityStrategy = builder.propertyVisibilityStrategy;
+        this.typeInheritanceConfiguration = builder.typeInheritanceConfiguration;
+    }
+
+    public static ClassCustomization empty() {
+        return EMPTY;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns instance of {@link JsonbCreator}.
+     *
+     * @return instance of creator
+     */
+    public JsonbCreator getCreator() {
+        return creator;
+    }
+
+    /**
+     * Names of properties to sort with.
+     *
+     * @return sorted names of properties
+     */
+    public String[] getPropertyOrder() {
+        return propertyOrder;
+    }
+
+    /**
+     * Property visibility strategy for this class model.
+     *
+     * @return visibility strategy
+     */
+    public PropertyVisibilityStrategy getPropertyVisibilityStrategy() {
+        return propertyVisibilityStrategy;
+    }
+
+    @Override
+    public JsonbNumberFormatter getSerializeNumberFormatter() {
+        return numberFormatter;
+    }
+
+    @Override
+    public JsonbNumberFormatter getDeserializeNumberFormatter() {
+        return numberFormatter;
+    }
+
+    @Override
+    public JsonbDateFormatter getSerializeDateFormatter() {
+        return dateTimeFormatter;
+    }
+
+    @Override
+    public JsonbDateFormatter getDeserializeDateFormatter() {
+        return dateTimeFormatter;
+    }
+
+    public TypeInheritanceConfiguration getPolymorphismConfig() {
+        return typeInheritanceConfiguration;
+    }
+
+    /**
+     * The customization builder that would be used to build an instance of {@link ClassCustomization} to ensure its immutability.
+     */
+    public static class Builder extends CustomizationBase.Builder<Builder, ClassCustomization> {
+
+        private JsonbCreator creator;
+        private String[] propertyOrder;
+        private JsonbNumberFormatter numberFormatter;
+        private JsonbDateFormatter dateTimeFormatter;
+        private PropertyVisibilityStrategy propertyVisibilityStrategy;
+        private TypeInheritanceConfiguration typeInheritanceConfiguration;
+
+        private Builder() {
+        }
+
+        @Override
+        public Builder of(ClassCustomization customization) {
+            super.of(customization);
+            creator(customization.creator);
+            propertyOrder(customization.propertyOrder);
+            numberFormatter(customization.numberFormatter);
+            dateTimeFormatter(customization.dateTimeFormatter);
+            propertyVisibilityStrategy(customization.propertyVisibilityStrategy);
+            return this;
+        }
+
+        public Builder creator(JsonbCreator creator) {
+            this.creator = creator;
+            return this;
+        }
+
+        public Builder propertyOrder(String[] propertyOrder) {
+            this.propertyOrder = propertyOrder;
+            return this;
+        }
+
+        public Builder numberFormatter(JsonbNumberFormatter numberFormatter) {
+            this.numberFormatter = numberFormatter;
+            return this;
+        }
+
+        public Builder dateTimeFormatter(JsonbDateFormatter dateTimeFormatter) {
+            this.dateTimeFormatter = dateTimeFormatter;
+            return this;
+        }
+
+        public Builder propertyVisibilityStrategy(PropertyVisibilityStrategy propertyVisibilityStrategy) {
+            this.propertyVisibilityStrategy = propertyVisibilityStrategy;
+            return this;
+        }
+
+        public Builder polymorphismConfig(TypeInheritanceConfiguration typeInheritanceConfiguration) {
+            this.typeInheritanceConfiguration = typeInheritanceConfiguration;
+            return this;
+        }
+
+        @Override
+        public ClassCustomization build() {
+            return new ClassCustomization(this);
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/ComponentBoundCustomization.java b/src/main/java/org/eclipse/yasson/internal/model/customization/ComponentBoundCustomization.java
new file mode 100644
index 0000000..e964ed8
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/ComponentBoundCustomization.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.components.DeserializerBinding;
+import org.eclipse.yasson.internal.components.SerializerBinding;
+
+/**
+ * Customization which is aware of bound components, such as adapters and (de)serializers.
+ */
+public interface ComponentBoundCustomization {
+
+    /**
+     * @return Adapter wrapper class with resolved generic information.
+     */
+    AdapterBinding getSerializeAdapterBinding();
+
+    /**
+     * @return Adapter wrapper class with resolved generic information.
+     */
+    AdapterBinding getDeserializeAdapterBinding();
+
+    /**
+     * Serializer wrapper with resolved generic info.
+     *
+     * @return serializer wrapper
+     */
+    SerializerBinding<?> getSerializerBinding();
+
+    /**
+     * Deserializer wrapper with resolved generic info.
+     *
+     * @return deserializer wrapper
+     */
+    DeserializerBinding<?> getDeserializerBinding();
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/CreatorCustomization.java b/src/main/java/org/eclipse/yasson/internal/model/customization/CreatorCustomization.java
new file mode 100644
index 0000000..a3c8631
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/CreatorCustomization.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+import org.eclipse.yasson.internal.model.PropertyModel;
+
+/**
+ * Customization for creator (constructor / factory methods) parameters.
+ */
+public class CreatorCustomization extends CustomizationBase {
+
+    private final JsonbNumberFormatter numberFormatter;
+    private final JsonbDateFormatter dateFormatter;
+    private final boolean required;
+    private PropertyModel propertyModel;
+
+    /**
+     * Creates new creator customization instance.
+     *
+     * @param builder builder of the customization
+     */
+    private CreatorCustomization(Builder builder) {
+        super(builder);
+        this.numberFormatter = builder.numberFormatter;
+        this.dateFormatter = builder.dateFormatter;
+        this.required = builder.required;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public JsonbNumberFormatter getSerializeNumberFormatter() {
+        throw new UnsupportedOperationException("Serialization is not supported for creator parameters.");
+    }
+
+    @Override
+    public JsonbNumberFormatter getDeserializeNumberFormatter() {
+        if (numberFormatter != null) {
+            return numberFormatter;
+        } else if (propertyModel != null) {
+            return propertyModel.getCustomization().getDeserializeNumberFormatter();
+        }
+        return null;
+    }
+
+    @Override
+    public JsonbDateFormatter getSerializeDateFormatter() {
+        throw new UnsupportedOperationException("Serialization is not supported for creator parameters.");
+    }
+
+    @Override
+    public JsonbDateFormatter getDeserializeDateFormatter() {
+        if (dateFormatter != null) {
+            return dateFormatter;
+        } else if (propertyModel != null) {
+            return propertyModel.getCustomization().getDeserializeDateFormatter();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean isNillable() {
+        throw new UnsupportedOperationException("Not supported for creator parameters.");
+    }
+
+    /**
+     * Set property referenced model.
+     *
+     * @param propertyModel referenced property model
+     */
+    public void setPropertyModel(PropertyModel propertyModel) {
+        this.propertyModel = propertyModel;
+    }
+
+    public boolean isRequired() {
+        return required;
+    }
+
+    public static final class Builder extends CustomizationBase.Builder<Builder, CreatorCustomization> {
+
+        private JsonbNumberFormatter numberFormatter;
+        private JsonbDateFormatter dateFormatter;
+        private boolean required = false;
+
+        private Builder() {
+        }
+
+        @Override
+        public Builder of(CreatorCustomization customization) {
+            super.of(customization);
+            numberFormatter = customization.numberFormatter;
+            dateFormatter = customization.dateFormatter;
+            return this;
+        }
+
+        public Builder numberFormatter(JsonbNumberFormatter numberFormatter) {
+            this.numberFormatter = numberFormatter;
+            return this;
+        }
+
+        public Builder dateFormatter(JsonbDateFormatter dateFormatter) {
+            this.dateFormatter = dateFormatter;
+            return this;
+        }
+
+        public Builder required(boolean required) {
+            this.required = required;
+            return this;
+        }
+
+        @Override
+        public CreatorCustomization build() {
+            return new CreatorCustomization(this);
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/Customization.java b/src/main/java/org/eclipse/yasson/internal/model/customization/Customization.java
new file mode 100644
index 0000000..7cd9060
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/Customization.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+
+/**
+ * Customization configuration for class or field.
+ * Configuration parsed from annotation is put here.
+ * Immutable.
+ */
+public interface Customization {
+
+    /**
+     * Number formatter for formatting numbers during serialization process. It could be the same formatter instance used for
+     * deserialization
+     * (returned by {@link #getDeserializeNumberFormatter()}
+     *
+     * @return number formatter
+     */
+    JsonbNumberFormatter getSerializeNumberFormatter();
+
+    /**
+     * Number formatter for formatting numbers during deserialization process. It could be the same formatter instance used for
+     * serialization
+     * (returned by {@link #getSerializeNumberFormatter()}
+     *
+     * @return number formatter
+     */
+    JsonbNumberFormatter getDeserializeNumberFormatter();
+
+    /**
+     * Date formatter for formatting date values during serialization process. It could be the same formatter instance used for
+     * deserialization
+     * (returned by {@link #getDeserializeDateFormatter()}. If not set, defaulted to <code>jakarta.json.bind.annotation
+     * .JsonbDateFormat.DEFAULT_FORMAT.
+     * </code>
+     *
+     * @return date formatter
+     */
+    JsonbDateFormatter getSerializeDateFormatter();
+
+    /**
+     * Date formatter for formatting date values during deserialization process. It could be the same formatter instance used
+     * for serialization
+     * (returned by {@link #getSerializeDateFormatter()}. If not set, defaulted to <code>jakarta.json.bind.annotation
+     * .JsonbDateFormat.DEFAULT_FORMAT.
+     * </code>
+     *
+     * @return date formatter
+     */
+    JsonbDateFormatter getDeserializeDateFormatter();
+
+    /**
+     * Returns true if <i>nillable</i> customization is present.
+     *
+     * @return True if <i>nillable</i> customization is present.
+     */
+    boolean isNillable();
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/CustomizationBase.java b/src/main/java/org/eclipse/yasson/internal/model/customization/CustomizationBase.java
new file mode 100644
index 0000000..2b49180
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/CustomizationBase.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.components.DeserializerBinding;
+import org.eclipse.yasson.internal.components.SerializerBinding;
+
+/**
+ * Common properties of {@link ClassCustomization} and {@link PropertyCustomization}.
+ */
+abstract class CustomizationBase implements Customization, ComponentBoundCustomization {
+
+    private final AdapterBinding adapterBinding;
+    private final SerializerBinding<?> serializerBinding;
+    private final DeserializerBinding<?> deserializerBinding;
+    private final boolean nillable;
+
+    /**
+     * Copies properties from builder an creates immutable instance.
+     *
+     * @param builder not null
+     */
+    CustomizationBase(Builder<?, ?> builder) {
+        this.nillable = builder.nillable;
+        this.adapterBinding = builder.adapterBinding;
+        this.serializerBinding = builder.serializerBinding;
+        this.deserializerBinding = builder.deserializerBinding;
+    }
+
+    /**
+     * Returns true if <i>nillable</i> customization is present.
+     *
+     * @return True if <i>nillable</i> customization is present.
+     */
+    public boolean isNillable() {
+        return nillable;
+    }
+
+    public AdapterBinding getSerializeAdapterBinding() {
+        return adapterBinding;
+    }
+
+    @Override
+    public AdapterBinding getDeserializeAdapterBinding() {
+        return adapterBinding;
+    }
+
+    /**
+     * Serializer wrapper with resolved generic info.
+     *
+     * @return serializer wrapper
+     */
+    public SerializerBinding<?> getSerializerBinding() {
+        return serializerBinding;
+    }
+
+    /**
+     * Deserializer wrapper with resolved generic info.
+     *
+     * @return deserializer wrapper
+     */
+    public DeserializerBinding<?> getDeserializerBinding() {
+        return deserializerBinding;
+    }
+
+    @SuppressWarnings("unchecked")
+    abstract static class Builder<T extends Builder<T, B>, B extends CustomizationBase> {
+
+        private AdapterBinding adapterBinding;
+        private SerializerBinding<?> serializerBinding;
+        private DeserializerBinding<?> deserializerBinding;
+        private boolean nillable;
+
+        Builder() {
+        }
+
+        public T of(B customization) {
+            adapterBinding = customization.getDeserializeAdapterBinding();
+            serializerBinding = customization.getSerializerBinding();
+            deserializerBinding = customization.getDeserializerBinding();
+            nillable = customization.isNillable();
+            return (T) this;
+        }
+
+        public T adapterBinding(AdapterBinding adapterBinding) {
+            this.adapterBinding = adapterBinding;
+            return (T) this;
+        }
+
+        public T serializerBinding(SerializerBinding<?> serializerBinding) {
+            this.serializerBinding = serializerBinding;
+            return (T) this;
+        }
+
+        public T deserializerBinding(DeserializerBinding<?> deserializerBinding) {
+            this.deserializerBinding = deserializerBinding;
+            return (T) this;
+        }
+
+        public T nillable(boolean nillable) {
+            this.nillable = nillable;
+            return (T) this;
+        }
+
+        public abstract B build();
+
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyCustomization.java b/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyCustomization.java
new file mode 100644
index 0000000..022e458
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyCustomization.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+
+/**
+ * Customization for a property of a class.
+ */
+public class PropertyCustomization extends CustomizationBase {
+
+    private final String jsonReadName;
+    private final String jsonWriteName;
+
+    private final JsonbNumberFormatter serializeNumberFormatter;
+    private final JsonbNumberFormatter deserializeNumberFormatter;
+
+    private final JsonbDateFormatter serializeDateFormatter;
+    private final JsonbDateFormatter deserializeDateFormatter;
+
+    private final AdapterBinding serializeAdapter;
+    private final AdapterBinding deserializeAdapter;
+
+    private final boolean readTransient;
+    private final boolean writeTransient;
+
+    private final Class<?> implementationClass;
+
+    /**
+     * Copies properties from builder an creates immutable instance.
+     *
+     * @param builder not null
+     */
+    private PropertyCustomization(Builder builder) {
+        super(builder);
+        this.serializeAdapter = builder.serializeAdapter;
+        this.deserializeAdapter = builder.deserializeAdapter;
+        this.jsonReadName = builder.jsonReadName;
+        this.jsonWriteName = builder.jsonWriteName;
+        this.serializeNumberFormatter = builder.serializeNumberFormatter;
+        this.deserializeNumberFormatter = builder.deserializeNumberFormatter;
+        this.serializeDateFormatter = builder.serializeDateFormatter;
+        this.deserializeDateFormatter = builder.deserializeDateFormatter;
+        this.readTransient = builder.readTransient;
+        this.writeTransient = builder.writeTransient;
+        this.implementationClass = builder.implementationClass;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Name if specified for property setter with {@link jakarta.json.bind.annotation.JsonbProperty}.
+     *
+     * @return read name
+     */
+    public String getJsonReadName() {
+        return jsonReadName;
+    }
+
+    /**
+     * Name if specified for property getter with {@link jakarta.json.bind.annotation.JsonbProperty}.
+     *
+     * @return write name
+     */
+    public String getJsonWriteName() {
+        return jsonWriteName;
+    }
+
+    @Override
+    public JsonbNumberFormatter getSerializeNumberFormatter() {
+        return serializeNumberFormatter;
+    }
+
+    @Override
+    public JsonbNumberFormatter getDeserializeNumberFormatter() {
+        return deserializeNumberFormatter;
+    }
+
+    @Override
+    public JsonbDateFormatter getSerializeDateFormatter() {
+        return serializeDateFormatter;
+    }
+
+    @Override
+    public JsonbDateFormatter getDeserializeDateFormatter() {
+        return deserializeDateFormatter;
+    }
+
+    /**
+     * The flag indicating whether the value of the underlying type/property should be processed during serialization process
+     * or not.
+     *
+     * @return true indicates that the underlying type/property should be included in serialization process and false indicates
+     * it should not
+     */
+    public boolean isReadTransient() {
+        return readTransient;
+    }
+
+    /**
+     * The flag indicating whether the value of the underlying type/property should be processed during deserialization process
+     * or not.
+     *
+     * @return true indicates that the underlying type/property should be included in deserialization process and false
+     * indicates it should not
+     */
+    public boolean isWriteTransient() {
+        return writeTransient;
+    }
+
+    /**
+     * Implementation class if property is interface type.
+     *
+     * @return class implementing property interface
+     */
+    public Class<?> getImplementationClass() {
+        return implementationClass;
+    }
+
+    @Override
+    public AdapterBinding getDeserializeAdapterBinding() {
+        return deserializeAdapter;
+    }
+
+    @Override
+    public AdapterBinding getSerializeAdapterBinding() {
+        return serializeAdapter;
+    }
+
+    public static final class Builder extends CustomizationBase.Builder<Builder, PropertyCustomization> {
+
+        private String jsonReadName;
+        private String jsonWriteName;
+        private JsonbNumberFormatter serializeNumberFormatter;
+        private JsonbNumberFormatter deserializeNumberFormatter;
+        private JsonbDateFormatter serializeDateFormatter;
+        private JsonbDateFormatter deserializeDateFormatter;
+        private AdapterBinding serializeAdapter;
+        private AdapterBinding deserializeAdapter;
+        private boolean readTransient;
+        private boolean writeTransient;
+        private Class<?> implementationClass;
+
+        private Builder() {
+        }
+
+        @Override
+        public Builder of(PropertyCustomization customization) {
+            jsonReadName = customization.jsonReadName;
+            jsonWriteName = customization.jsonWriteName;
+            serializeNumberFormatter = customization.serializeNumberFormatter;
+            deserializeNumberFormatter = customization.deserializeNumberFormatter;
+            serializeDateFormatter = customization.serializeDateFormatter;
+            deserializeDateFormatter = customization.deserializeDateFormatter;
+            serializeAdapter = customization.serializeAdapter;
+            deserializeAdapter = customization.deserializeAdapter;
+            readTransient = customization.readTransient;
+            writeTransient = customization.writeTransient;
+            implementationClass = customization.implementationClass;
+            return super.of(customization);
+        }
+
+        /**
+         * Set a JSON property name used to read a property value from on deserialization.
+         *
+         * @param jsonReadName JSON property name
+         */
+        public Builder jsonReadName(String jsonReadName) {
+            this.jsonReadName = jsonReadName;
+            return this;
+        }
+
+        /**
+         * Set a property name which is written to JSON document on serialization.
+         *
+         * @param jsonWriteName Property name.
+         */
+        public Builder jsonWriteName(String jsonWriteName) {
+            this.jsonWriteName = jsonWriteName;
+            return this;
+        }
+
+        /**
+         * Set number formatter for formatting numbers during serialization process.
+         *
+         * @param serializeNumberFormatter Number formatter for formatting numbers during serialization process.
+         */
+        public Builder serializeNumberFormatter(JsonbNumberFormatter serializeNumberFormatter) {
+            this.serializeNumberFormatter = serializeNumberFormatter;
+            return this;
+        }
+
+        /**
+         * Set number formatter for formatting numbers during deserialization process.
+         *
+         * @param deserializeNumberFormatter Number formatter for formatting numbers during deserialization process.
+         */
+        public Builder deserializeNumberFormatter(JsonbNumberFormatter deserializeNumberFormatter) {
+            this.deserializeNumberFormatter = deserializeNumberFormatter;
+            return this;
+        }
+
+        /**
+         * Set date formatter for formatting dates during serialization process.
+         *
+         * @param serializeDateFormatter Date formatter for formatting dates during serialization process.
+         */
+        public Builder serializeDateFormatter(JsonbDateFormatter serializeDateFormatter) {
+            this.serializeDateFormatter = serializeDateFormatter;
+            return this;
+        }
+
+        /**
+         * Set date formatter for formatting dates during deserialization process.
+         *
+         * @param deserializeDateFormatter Date formatter for formatting dates during deserialization process.
+         */
+        public Builder deserializeDateFormatter(JsonbDateFormatter deserializeDateFormatter) {
+            this.deserializeDateFormatter = deserializeDateFormatter;
+            return this;
+        }
+
+        public Builder serializeAdapter(AdapterBinding serializeAdapter) {
+            this.serializeAdapter = serializeAdapter;
+            return this;
+        }
+
+        public Builder deserializeAdapter(AdapterBinding deserializeAdapter) {
+            this.deserializeAdapter = deserializeAdapter;
+            return this;
+        }
+
+        /**
+         * Sets a presence of <i>read transient</i> customization.
+         *
+         * @param readTransient Presence of <i>read transient</i> customization.
+         */
+        public Builder readTransient(boolean readTransient) {
+            this.readTransient = readTransient;
+            return this;
+        }
+
+        public boolean readTransient() {
+            return readTransient;
+        }
+
+        /**
+         * Sets a presence of <i>write transient</i> customization.
+         *
+         * @param writeTransient Presence of <i>write transient</i> customization.
+         */
+        public Builder writeTransient(boolean writeTransient) {
+            this.writeTransient = writeTransient;
+            return this;
+        }
+
+        public boolean writeTransient() {
+            return writeTransient;
+        }
+
+        /**
+         * Implementation class if property is interface type.
+         *
+         * @param implementationClass implementing property interface
+         */
+        public Builder implementationClass(Class<?> implementationClass) {
+            this.implementationClass = implementationClass;
+            return this;
+        }
+
+        @Override
+        public PropertyCustomization build() {
+            return new PropertyCustomization(this);
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyOrdering.java b/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyOrdering.java
new file mode 100644
index 0000000..b720c3b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/PropertyOrdering.java
@@ -0,0 +1,74 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.config.PropertyOrderStrategy;
+
+import org.eclipse.yasson.internal.model.ClassModel;
+import org.eclipse.yasson.internal.model.PropertyModel;
+
+/**
+ * Order properties in bean object. {@link jakarta.json.bind.annotation.JsonbPropertyOrder} have always precedence.
+ * If configured with {@link JsonbConfig} provided property order strategy will be used.
+ */
+public class PropertyOrdering {
+
+    private final Consumer<List<PropertyModel>> propertyOrderStrategy;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param propertyOrderStrategy Property order strategy. Must be not null.
+     */
+    public PropertyOrdering(Consumer<List<PropertyModel>> propertyOrderStrategy) {
+        this.propertyOrderStrategy = Objects.requireNonNull(propertyOrderStrategy);
+    }
+
+    /**
+     * Sorts class properties either, by class {@link jakarta.json.bind.annotation.JsonbPropertyOrder} annotation,
+     * or by {@link PropertyOrderStrategy} if set in {@link JsonbConfig}.
+     *
+     * @param properties Properties to sort.
+     * @param classModel Class model.
+     * @return Sorted list of properties.
+     */
+    public List<PropertyModel> orderProperties(List<PropertyModel> properties, ClassModel classModel) {
+        Map<String, PropertyModel> byReadName = new HashMap<>();
+        properties.forEach(propertyModel -> byReadName.put(propertyModel.getPropertyName(), propertyModel));
+
+        String[] order = classModel.getClassCustomization().getPropertyOrder();
+        List<PropertyModel> sortedProperties = new ArrayList<>();
+        if (order != null) {
+            //if @JsonbPropertyOrder annotation is defined on a class
+            for (String propName : order) {
+                final PropertyModel remove = byReadName.remove(propName);
+                if (remove != null) {
+                    sortedProperties.add(remove);
+                }
+            }
+        }
+
+        List<PropertyModel> readNamesToSort = new ArrayList<>(byReadName.values());
+        propertyOrderStrategy.accept(readNamesToSort);
+        sortedProperties.addAll(readNamesToSort);
+        return sortedProperties;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/StrategiesProvider.java b/src/main/java/org/eclipse/yasson/internal/model/customization/StrategiesProvider.java
new file mode 100644
index 0000000..4855daa
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/StrategiesProvider.java
@@ -0,0 +1,147 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import java.nio.CharBuffer;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+
+import org.eclipse.yasson.internal.model.PropertyModel;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import static java.util.Comparator.comparing;
+
+import static jakarta.json.bind.config.PropertyNamingStrategy.CASE_INSENSITIVE;
+import static jakarta.json.bind.config.PropertyNamingStrategy.IDENTITY;
+import static jakarta.json.bind.config.PropertyNamingStrategy.LOWER_CASE_WITH_DASHES;
+import static jakarta.json.bind.config.PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES;
+import static jakarta.json.bind.config.PropertyNamingStrategy.UPPER_CAMEL_CASE;
+import static jakarta.json.bind.config.PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES;
+import static jakarta.json.bind.config.PropertyOrderStrategy.ANY;
+import static jakarta.json.bind.config.PropertyOrderStrategy.LEXICOGRAPHICAL;
+import static jakarta.json.bind.config.PropertyOrderStrategy.REVERSE;
+
+/**
+ * Provides strategies for {@link jakarta.json.bind.config.PropertyNamingStrategy} and
+ * {@link jakarta.json.bind.config.PropertyOrderStrategy}.
+ */
+public final class StrategiesProvider {
+    private StrategiesProvider() {
+    }
+
+    /**
+     * Case insensitive naming strategy.
+     */
+    public static final PropertyNamingStrategy CASE_INSENSITIVE_STRATEGY = Objects::requireNonNull;
+
+    /**
+     * Returns an ordering strategy which corresponds to the ordering strategy name.
+     *
+     * @param strategy ordering strategy name
+     * @return ordering strategy
+     */
+    public static Consumer<List<PropertyModel>> getOrderingFunction(String strategy) {
+        switch (strategy) {
+        case LEXICOGRAPHICAL:
+            return props -> props.sort(comparing(PropertyModel::getWriteName));
+        case ANY:
+            return props -> {
+            };
+        case REVERSE:
+            return props -> props.sort(comparing(PropertyModel::getWriteName).reversed());
+        default:
+            throw new JsonbException(Messages.getMessage(MessageKeys.PROPERTY_ORDER, strategy));
+        }
+    }
+
+    /**
+     * Returns a naming strategy which corresponds to the naming strategy name.
+     *
+     * @param strategy naming strategy name
+     * @return naming strategy
+     */
+    public static PropertyNamingStrategy getPropertyNamingStrategy(String strategy) {
+        switch (strategy) {
+        case LOWER_CASE_WITH_UNDERSCORES:
+            return createLowerCaseStrategyWithSeparator('_');
+        case LOWER_CASE_WITH_DASHES:
+            return createLowerCaseStrategyWithSeparator('-');
+        case UPPER_CAMEL_CASE:
+            return createUpperCamelCaseStrategy();
+        case UPPER_CAMEL_CASE_WITH_SPACES:
+            return createUpperCamelCaseWithSpaceStrategy();
+        case IDENTITY:
+            return Objects::requireNonNull;
+        case CASE_INSENSITIVE:
+            return CASE_INSENSITIVE_STRATEGY;
+        default:
+            throw new JsonbException("No property naming strategy was found for: " + strategy);
+        }
+    }
+
+    private static PropertyNamingStrategy createUpperCamelCaseStrategy() {
+        return propertyName -> {
+            Objects.requireNonNull(propertyName);
+            char first = Character.toUpperCase(propertyName.charAt(0));
+
+            return first + propertyName.substring(1);
+        };
+    }
+
+    private static PropertyNamingStrategy createUpperCamelCaseWithSpaceStrategy() {
+        return propertyName -> {
+            String upperCased = createUpperCamelCaseStrategy().translateName(propertyName);
+            CharBuffer buffer = CharBuffer.allocate(upperCased.length() * 2);
+            char last = Character.MIN_VALUE;
+
+            for (int i = 0; i < upperCased.length(); ++i) {
+                char current = upperCased.charAt(i);
+
+                if (i > 0 && Character.isUpperCase(current) && isLowerCaseCharacter(last)) {
+                    buffer.append(' ');
+                }
+                last = current;
+                buffer.append(current);
+            }
+            return new String(buffer.array(), 0, buffer.position());
+        };
+    }
+
+    private static PropertyNamingStrategy createLowerCaseStrategyWithSeparator(char separator) {
+        return propertyName -> {
+            Objects.requireNonNull(propertyName);
+            CharBuffer charBuffer = CharBuffer.allocate(propertyName.length() * 2);
+            char last = Character.MIN_VALUE;
+
+            for (int i = 0; i < propertyName.length(); ++i) {
+                char current = propertyName.charAt(i);
+
+                if (i > 0 && Character.isUpperCase(current) && isLowerCaseCharacter(last)) {
+                    charBuffer.append(separator);
+                }
+                last = current;
+                charBuffer.append(Character.toLowerCase(current));
+            }
+            return new String(charBuffer.array(), 0, charBuffer.position());
+        };
+    }
+
+    private static boolean isLowerCaseCharacter(char character) {
+        return Character.isAlphabetic(character) && Character.isLowerCase(character);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/TypeInheritanceConfiguration.java b/src/main/java/org/eclipse/yasson/internal/model/customization/TypeInheritanceConfiguration.java
new file mode 100644
index 0000000..48308d4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/TypeInheritanceConfiguration.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import jakarta.json.bind.annotation.JsonbTypeInfo;
+
+/**
+ * Type inheritance configuration.
+ */
+public class TypeInheritanceConfiguration {
+
+    private final String fieldName;
+    private final boolean inherited;
+    private final Map<Class<?>, String> aliases;
+    private final Class<?> definedType;
+    private final TypeInheritanceConfiguration parentConfig;
+
+    private TypeInheritanceConfiguration(Builder builder) {
+        this.fieldName = builder.fieldName;
+        this.inherited = builder.inherited;
+        this.aliases = Map.copyOf(builder.aliases);
+        this.parentConfig = builder.parentConfig;
+        this.definedType = builder.definedType;
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    public boolean isInherited() {
+        return inherited;
+    }
+
+    public Map<Class<?>, String> getAliases() {
+        return aliases;
+    }
+
+    public Class<?> getDefinedType() {
+        return definedType;
+    }
+
+    public TypeInheritanceConfiguration getParentConfig() {
+        return parentConfig;
+    }
+
+    public static final class Builder {
+
+        private Map<Class<?>, String> aliases = new HashMap<>();
+        private String fieldName = JsonbTypeInfo.DEFAULT_KEY_NAME;
+        private boolean inherited = false;
+        private Class<?> definedType;
+        private TypeInheritanceConfiguration parentConfig;
+
+        private Builder() {
+        }
+
+        public Builder inherited(boolean inherited) {
+            this.inherited = inherited;
+            return this;
+        }
+
+        public Builder fieldName(String fieldName) {
+            this.fieldName = Objects.requireNonNull(fieldName);
+            return this;
+        }
+
+        public Builder alias(Class<?> clazz, String alias) {
+            this.aliases.put(clazz, alias);
+            return this;
+        }
+
+        public Builder parentConfig(TypeInheritanceConfiguration parentConfig) {
+            this.parentConfig = parentConfig;
+            return this;
+        }
+
+        public Builder definedType(Class<?> definedType) {
+            this.definedType = definedType;
+            return this;
+        }
+
+        public Builder of(TypeInheritanceConfiguration typeInheritanceConfiguration) {
+            this.fieldName = typeInheritanceConfiguration.fieldName;
+            this.aliases = new HashMap<>(typeInheritanceConfiguration.aliases);
+            this.inherited = typeInheritanceConfiguration.inherited;
+            this.parentConfig = typeInheritanceConfiguration.parentConfig;
+            this.definedType = typeInheritanceConfiguration.definedType;
+            return this;
+        }
+
+        public TypeInheritanceConfiguration build() {
+            return new TypeInheritanceConfiguration(this);
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/model/customization/VisibilityStrategiesProvider.java b/src/main/java/org/eclipse/yasson/internal/model/customization/VisibilityStrategiesProvider.java
new file mode 100644
index 0000000..9967bb5
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/model/customization/VisibilityStrategiesProvider.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Provider of the JSON-B visibility strategies.
+ */
+public class VisibilityStrategiesProvider {
+
+    private static final PropertyVisibilityStrategy PUBLIC_PROPERTY = new PublicPropertyVisibilityStrategy();
+    private static final PropertyVisibilityStrategy PUBLIC_ACCESSOR_METHODS = new PublicAccessorVisibilityStrategy();
+    private static final PropertyVisibilityStrategy PUBLIC_FIELDS = new PublicFieldsVisibilityStrategy();
+    private static final PropertyVisibilityStrategy ALL_FIELDS_AND_METHODS = new AllFieldsVisibilityStrategy();
+
+    private VisibilityStrategiesProvider() {
+        throw new IllegalStateException("This class cannot be instantiated");
+    }
+
+    public static PropertyVisibilityStrategy getStrategy(String strategy) {
+        switch (strategy) {
+        case "PUBLIC_PROPERTY":
+            return PUBLIC_PROPERTY;
+        case "PUBLIC_ACCESSOR_METHODS":
+            return PUBLIC_ACCESSOR_METHODS;
+        case "PUBLIC_FIELDS":
+            return PUBLIC_FIELDS;
+        case "ALL_FIELD_AND_ACCESSORS":
+            return ALL_FIELDS_AND_METHODS;
+        default:
+            throw new JsonbException(Messages.getMessage(MessageKeys.UNKNOWN_VISIBILITY_STRATEGY, strategy));
+        }
+    }
+
+    private static final class PublicPropertyVisibilityStrategy implements PropertyVisibilityStrategy {
+        @Override
+        public boolean isVisible(Field field) {
+            return Modifier.isPublic(field.getModifiers());
+        }
+
+        @Override
+        public boolean isVisible(Method method) {
+            return Modifier.isPublic(method.getModifiers());
+        }
+    }
+
+    private static final class PublicAccessorVisibilityStrategy implements PropertyVisibilityStrategy {
+
+        @Override
+        public boolean isVisible(Field field) {
+            return false;
+        }
+
+        @Override
+        public boolean isVisible(Method method) {
+            return Modifier.isPublic(method.getModifiers());
+        }
+
+    }
+
+    private static final class PublicFieldsVisibilityStrategy implements PropertyVisibilityStrategy {
+
+        @Override
+        public boolean isVisible(Field field) {
+            return Modifier.isPublic(field.getModifiers());
+        }
+
+        @Override
+        public boolean isVisible(Method method) {
+            return false;
+        }
+
+    }
+
+    private static final class AllFieldsVisibilityStrategy implements PropertyVisibilityStrategy {
+        @Override
+        public boolean isVisible(Field field) {
+            return true;
+        }
+
+        @Override
+        public boolean isVisible(Method method) {
+            return true;
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java b/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
new file mode 100644
index 0000000..d08377c
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/properties/MessageKeys.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.properties;
+
+import java.beans.ConstructorProperties;
+import java.lang.reflect.ParameterizedType;
+import java.sql.Date;
+
+import jakarta.json.JsonNumber;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+/**
+ * Contains all message keys present in language property files.
+ */
+public enum MessageKeys {
+
+    /**
+     * An error has occurred while json deserialization.
+     */
+    PROCESS_FROM_JSON("processFromJson"),
+    /**
+     * An error has occurred while object serialization.
+     */
+    PROCESS_TO_JSON("processToJson"),
+    /**
+     * Object cannot be serialized.
+     */
+    CANNOT_MARSHAL_OBJECT("cannotMarshallObject"),
+    /**
+     * An error has occurred during runtime type resolution.
+     */
+    TYPE_RESOLUTION_ERROR("typeResolutionError"),
+    /**
+     * Generic bound not found.
+     */
+    GENERIC_BOUND_NOT_FOUND("genericBoundNotFound"),
+    /**
+     * {@link JsonValue} could not be converted to some type.
+     */
+    CANT_CONVERT_JSON_VALUE("cantConvertJsonValue"),
+    /**
+     * Root instance could not be created.
+     */
+    CANT_CREATE_ROOT_INSTANCE("cantCreateRootInstance"),
+    /**
+     * Class does not have default constructor.
+     */
+    NO_DEFAULT_CONSTRUCTOR("noDefaultConstructor"),
+    /**
+     * Class does not have default constructor.
+     */
+    RECORD_MULTIPLE_CONSTRUCTORS("recordMultipleConstructors"),
+    /**
+     * There has been an error while invoking getter.
+     */
+    INVOKING_GETTER("invokingGetter"),
+    /**
+     * Could not get field value.
+     */
+    GETTING_VALUE("gettingValue"),
+    /**
+     * Could not set field value.
+     */
+    SETTING_VALUE("settingValue"),
+    /**
+     * No logger name provided.
+     */
+    NO_LOGGER_NAME("noLoggerName"),
+    /**
+     * {@link ParameterizedType} superclass could not be resolved.
+     */
+    RESOLVE_PARAMETRIZED_TYPE("resolveParametrizedType"),
+    /**
+     * Instance could not be created.
+     */
+    CANT_CREATE_INSTANCE("cantCreateInstance"),
+    /**
+     * Type could not be inferred to deserialization.
+     */
+    INFER_TYPE_FOR_UNMARSHALL("inferTypeForUnmarshall"),
+    /**
+     * Implementation class is not compatible.
+     */
+    IMPL_CLASS_INCOMPATIBLE("implClassIncompatible"),
+    /**
+     * Value is not of target type.
+     */
+    NOT_VALUE_TYPE("notValueType"),
+    /**
+     * Unexpected parser event has occurred.
+     */
+    UNEXPECTED_PARSE_EVENT("unexpectedParseEvent"),
+    /**
+     * Am error has occurred while creating handles.
+     */
+    CREATING_HANDLES("creatingHandles"),
+    /**
+     * Could not get field value with method.
+     */
+    GETTING_VALUE_WITH("gettingValueWith"),
+    /**
+     * Could not set field value with method.
+     */
+    SETTING_VALUE_WITH("settingValueWith"),
+    /**
+     * String contains unpaired surrogate.
+     */
+    UNPAIRED_SURROGATE("unpairedSurrogate"),
+    /**
+     * An exception occurred while adapting object.
+     */
+    ADAPTER_EXCEPTION("adapterException"),
+    /**
+     * Adapter for current type has been found.
+     */
+    ADAPTER_FOUND("adapterFound"),
+    /**
+     * Adapter is incompatible for current type.
+     */
+    ADAPTER_INCOMPATIBLE("adapterIncompatible"),
+    /**
+     * Property order strategy not recognized.
+     */
+    PROPERTY_ORDER("propertyOrder"),
+    /**
+     * Unsupported Jsonp serializer value.
+     */
+    UNSUPPORTED_JSONP_SERIALIZER_VALUE("unsupportedJsonpSerializerValue"),
+    /**
+     * {@link JsonbConfig#FORMATTING} supports only Boolean types.
+     */
+    JSONB_CONFIG_FORMATTING_ILLEGAL_VALUE("Only Boolean type values are supported for JsonbConfig.FORMATTING property."),
+    /**
+     * No JNDI provider found.
+     */
+    NO_JNDI_ENVIRONMENT("noJndiEnvironment"),
+    /**
+     * CDI API provider has not been found.
+     */
+    NO_CDI_API_PROVIDER("noCdiApiProvider"),
+    /**
+     * Insufficient permissions to access property.
+     */
+    ILLEGAL_ACCESS("illegalAccess"),
+    /**
+     * CDI bean manager not found, serializers and adapters will not have CDI support.
+     */
+    BEAN_MANAGER_NOT_FOUND_USING_DEFAULT("usingDefaultConstructorInstantiator"),
+    /**
+     * CDI environment is not available.
+     */
+    NO_CDI_ENVIRONMENT("noCdiEnvironment"),
+    /**
+     * Cannot serialize single value due to I-Json support is enabled.
+     */
+    IJSON_ENABLED_SINGLE_VALUE("iJsonEnabledSingleValue"),
+    /**
+     * Property not found in target class.
+     */
+    PROPERTY_NOT_FOUND_DESERIALIZER("propertyNotFoundDeserializer"),
+    /**
+     * Property could not be set to target property.
+     */
+    SETTING_PROPERTY_DESERIALIZER("settingPropertyDeserializer"),
+    /**
+     * Loading of specific class in not allowed.
+     */
+    CLASS_LOAD_NOT_ALLOWED("classLoadNotAllowed"),
+    /**
+     * Data type is not supported.
+     */
+    UNSUPPORTED_DATE_TYPE("dateTypeNotSupported"),
+    /**
+     * There has been an error during parsing number.
+     */
+    DATE_PARSE_ERROR("errorParsingDate"),
+    /**
+     * Parsing offset date from epoch millisecond, UTC zone offset will be used.
+     */
+    OFFSET_DATE_TIME_FROM_MILLIS("offsetDateTimeFromMillis"),
+    /**
+     * Target date object could not be converted to or from epoch millis.
+     */
+    TIME_TO_EPOCH_MILLIS_ERROR("timeToEpochMillisError"),
+    /**
+     * Jsonb config property contains invalid type.
+     */
+    JSONB_CONFIG_PROPERTY_INVALID_TYPE("configPropertyInvalidType"),
+    /**
+     * Conversion target type from or to String is not supported.
+     */
+    CONVERSION_NOT_SUPPORTED("conversionNotSupported"),
+    /**
+     * End of the json structure reached.
+     */
+    END_OF_JSON_STRUCTURE("endOfJsonStructure"),
+    /**
+     * Json value type could not be deserialized to the target type.
+     */
+    INVALID_DESERIALIZATION_JSON_TYPE("invalidDeserializationType"),
+    /**
+     * An error occurred while calling {@link JsonbCreator}.
+     */
+    ERROR_CALLING_JSONB_CREATOR("errorCallingJsonbCreator"),
+    /**
+     * Return type of the {@link JsonbCreator} has to be the same as target type.
+     */
+    INCOMPATIBLE_FACTORY_CREATOR_RETURN_TYPE("incompatibleFactoryCreatorReturnType"),
+    /**
+     * Only one {@link JsonbCreator} can be present in the class.
+     */
+    MULTIPLE_JSONB_CREATORS("multipleJsonbCreators"),
+    /**
+     * An internal error has occurred.
+     */
+    INTERNAL_ERROR("internalError"),
+    /**
+     * There has been an error during property serialization.
+     */
+    SERIALIZE_PROPERTY_ERROR("serializePropertyError"),
+    /**
+     * There has been an error during value deserialization.
+     */
+    DESERIALIZE_VALUE_ERROR("deserializeValueError"),
+    /**
+     * Number has unsupported format.
+     */
+    PARSING_NUMBER("parsingNumber"),
+    /**
+     * Unknown binary data strategy selected.
+     */
+    UNKNOWN_BINARY_DATA_STRATEGY("unknownBinaryDataStrategy"),
+    /**
+     * Invalid property naming strategy selected.
+     */
+    PROPERTY_NAMING_STRATEGY_INVALID("invalidPropertyNamingStrategy"),
+    /**
+     * Creator parameter has to be annotated by {@link JsonbProperty} annotation.
+     */
+    CREATOR_PARAMETER_NOT_ANNOTATED("creatorParameterNotAnnotated"),
+    /**
+     * Json property could not be mapped to the target class.
+     */
+    UNKNOWN_JSON_PROPERTY("unknownJsonProperty"),
+    /**
+     * Json does not contain all necessary properties for {@link JsonbCreator}.
+     */
+    JSONB_CREATOR_MISSING_PROPERTY("jsonbCreatorMissingProperty"),
+    /**
+     * There has been an error during zone deserialization.
+     */
+    ZONE_PARSE_ERROR("zoneParseError"),
+    /**
+     * {@link JsonbTransient} was not the only annotation on class property.
+     */
+    JSONB_TRANSIENT_WITH_OTHER_ANNOTATIONS("jsonbTransientWithOtherAnnotations"),
+    /**
+     * Target type is not {@link ParameterizedType}.
+     */
+    NON_PARAMETRIZED_TYPE("nonParametrizedType"),
+    /**
+     * Handled property has the same read/write name in target class as some other property present there.
+     */
+    PROPERTY_NAME_CLASH("propertyNameClash"),
+    /**
+     * {@link Date} is not supported I-Json is enabled.
+     */
+    SQL_DATE_IJSON_ERROR("sqlDateIJsonError"),
+    /**
+     * Recursive reference detected.
+     */
+    RECURSIVE_REFERENCE("recursiveReference"),
+    /**
+     * An error occurred while DatatypeFactory creation.
+     */
+    DATATYPE_FACTORY_CREATION_FAILED("datatypeFactoryCreationFailed"),
+    /**
+     * Bean manager provider not found.
+     */
+    BEAN_MANAGER_PROVIDER_NOT_FOUND("beanManagerProviderNotFound"),
+    /**
+     * More than one constructor annotated with {@link ConstructorProperties} declared in target class.
+     */
+    MULTIPLE_CONSTRUCTOR_PROPERTIES_CREATORS("multipleConstructorPropertiesCreators"),
+    /**
+     * Target annotation is not visible in modules or classpath.
+     */
+    ANNOTATION_NOT_AVAILABLE("annotationNotAvailable"),
+    /**
+     * Missing value property in target annotation.
+     */
+    MISSING_VALUE_PROPERTY_IN_ANNOTATION("missingValuePropertyInAnnotation"),
+    /**
+     * Target json value is not valid {@link JsonNumber}.
+     */
+    NUMBER_INCOMPATIBLE_VALUE_TYPE_ARRAY("numberIncompatibleValueTypeArray"),
+    /**
+     * Target json value is not valid {@link JsonNumber}.
+     */
+    NUMBER_INCOMPATIBLE_VALUE_TYPE_OBJECT("numberIncompatibleValueTypeObject"),
+    /**
+     * Unknown visibility strategy.
+     */
+    UNKNOWN_VISIBILITY_STRATEGY("unknownVisibilityStrategy");
+
+    /**
+     * Message bundle key.
+     */
+    private final String key;
+
+    /**
+     * Creates an instance of message bundle key.
+     *
+     * @param key Message key from bundle.
+     */
+    MessageKeys(final String key) {
+        this.key = key;
+    }
+
+    /**
+     * Returns message bundle key.
+     *
+     * @return message bundle key
+     */
+    public String getKey() {
+        return key;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/properties/Messages.java b/src/main/java/org/eclipse/yasson/internal/properties/Messages.java
new file mode 100644
index 0000000..8c03514
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/properties/Messages.java
@@ -0,0 +1,104 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.properties;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.PropertyResourceBundle;
+import java.util.ResourceBundle;
+
+/**
+ * JSON-B messages.
+ */
+public class Messages {
+
+    private static final String MESSAGE_BUNDLE = "yasson-messages";
+    private static final String ENCODING = "UTF-8";
+
+    private Messages() {
+    }
+
+    /**
+     * Gets message by key. Default locale is used.
+     *
+     * @param key     Message key.
+     * @param objects Message parameters.
+     * @return Formatted message in string.
+     */
+    public static String getMessage(MessageKeys key, Object... objects) {
+        return getMessage(key, Locale.getDefault(), objects);
+    }
+
+    /**
+     * Gets message by key and locale.
+     *
+     * @param key     Message key.
+     * @param locale  Locale.
+     * @param objects Message parameters.
+     * @return Formatted message in string.
+     */
+    public static String getMessage(MessageKeys key, Locale locale, Object... objects) {
+        ResourceBundle messages = getResourceBundle(locale);
+        MessageFormat formatter = new MessageFormat(messages.getString(key.getKey()));
+        return formatter.format(objects);
+    }
+
+    /**
+     * ResourceBundle.Control is not supported when loaded from JPMS native module.
+     */
+    private static ResourceBundle getResourceBundle(Locale locale) {
+        try {
+            return ResourceBundle.getBundle(MESSAGE_BUNDLE, locale, new UTF8Control());
+        } catch (UnsupportedOperationException e) {
+            return ResourceBundle.getBundle(MESSAGE_BUNDLE, locale);
+        }
+    }
+
+    static class UTF8Control extends ResourceBundle.Control {
+        public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
+                throws IllegalAccessException, InstantiationException, IOException {
+            // The below is a copy of the default implementation.
+            String bundleName = toBundleName(baseName, locale);
+            String resourceName = toResourceName(bundleName, "properties");
+            ResourceBundle bundle = null;
+            InputStream stream = null;
+            if (reload) {
+                URL url = loader.getResource(resourceName);
+                if (url != null) {
+                    URLConnection connection = url.openConnection();
+                    if (connection != null) {
+                        connection.setUseCaches(false);
+                        stream = connection.getInputStream();
+                    }
+                }
+            } else {
+                stream = loader.getResourceAsStream(resourceName);
+            }
+            if (stream != null) {
+                try {
+                    // Only this line is changed to make it to read properties files as UTF-8.
+                    bundle = new PropertyResourceBundle(new InputStreamReader(stream, ENCODING));
+                } finally {
+                    stream.close();
+                }
+            }
+            return bundle;
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/AbstractSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/AbstractSerializer.java
new file mode 100644
index 0000000..6ed18d8
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/AbstractSerializer.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+/**
+ * Abstract model serializer with delegate.
+ */
+abstract class AbstractSerializer implements ModelSerializer {
+
+    final ModelSerializer delegate;
+
+    AbstractSerializer(ModelSerializer delegate) {
+        this.delegate = delegate;
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/AdapterSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/AdapterSerializer.java
new file mode 100644
index 0000000..582c581
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/AdapterSerializer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * User defined adapter invoker.
+ */
+class AdapterSerializer extends AbstractSerializer {
+
+    private final JsonbAdapter<Object, Object> adapter;
+    private final AdapterBinding adapterBinding;
+
+    @SuppressWarnings("unchecked")
+    AdapterSerializer(AdapterBinding adapterBinding,
+                      ModelSerializer delegate) {
+        super(delegate);
+        this.adapter = (JsonbAdapter<Object, Object>) adapterBinding.getAdapter();
+        this.adapterBinding = adapterBinding;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        try {
+            delegate.serialize(adapter.adaptToJson(value), generator, context);
+        } catch (Exception e) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.ADAPTER_EXCEPTION,
+                                                         adapterBinding.getBindingType(),
+                                                         adapterBinding.getToType(),
+                                                         adapter.getClass()), e);
+        }
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/ArraySerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/ArraySerializer.java
new file mode 100644
index 0000000..f538bc6
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/ArraySerializer.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.util.Base64;
+import java.util.Map;
+import java.util.function.Function;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.BinaryDataStrategy;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Array container serializer.
+ */
+abstract class ArraySerializer implements ModelSerializer {
+
+    private static final Map<Class<?>, Function<ModelSerializer, ArraySerializer>> ARRAY_SERIALIZERS;
+
+    static {
+        ARRAY_SERIALIZERS = Map.of(boolean[].class, BooleanArraySerializer::new,
+                                   byte[].class, ByteArraySerializer::new,
+                                   char[].class, CharacterArraySerializer::new,
+                                   double[].class, DoubleArraySerializer::new,
+                                   float[].class, FloatArraySerializer::new,
+                                   int[].class, IntegerArraySerializer::new,
+                                   long[].class, LongArraySerializer::new,
+                                   short[].class, ShortArraySerializer::new);
+    }
+
+    private final ModelSerializer valueSerializer;
+
+    protected ArraySerializer(ModelSerializer valueSerializer) {
+        this.valueSerializer = valueSerializer;
+    }
+
+    public static ModelSerializer create(Class<?> arrayType,
+                                         JsonbContext jsonbContext,
+                                         ModelSerializer modelSerializer) {
+        String binaryDataStrategy = jsonbContext.getConfigProperties().getBinaryDataStrategy();
+        if (byte[].class.equals(arrayType) && !binaryDataStrategy.equals(BinaryDataStrategy.BYTE)) {
+            return new Base64ByteArraySerializer(binaryDataStrategy);
+        }
+        if (ARRAY_SERIALIZERS.containsKey(arrayType)) {
+            return ARRAY_SERIALIZERS.get(arrayType).apply(modelSerializer);
+        }
+        return new ObjectArraySerializer(modelSerializer);
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.writeStartArray();
+        serializeArray(value, generator, context);
+        generator.writeEnd();
+    }
+
+    abstract void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context);
+
+    protected ModelSerializer getValueSerializer() {
+        return valueSerializer;
+    }
+
+    private static final class ByteArraySerializer extends ArraySerializer {
+
+        ByteArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            byte[] array = (byte[]) value;
+            for (byte b : array) {
+                getValueSerializer().serialize(b, generator, context);
+            }
+        }
+
+    }
+
+    private static final class Base64ByteArraySerializer implements ModelSerializer {
+
+        private final Base64.Encoder encoder;
+
+        Base64ByteArraySerializer(String strategy) {
+            this.encoder = getEncoder(strategy);
+        }
+
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            byte[] array = (byte[]) value;
+            generator.write(encoder.encodeToString(array));
+        }
+
+        private Base64.Encoder getEncoder(String strategy) {
+            switch (strategy) {
+            case BinaryDataStrategy.BASE_64:
+                return Base64.getEncoder();
+            case BinaryDataStrategy.BASE_64_URL:
+                return Base64.getUrlEncoder();
+            default:
+                throw new JsonbException(Messages.getMessage(MessageKeys.INTERNAL_ERROR, "Invalid strategy: " + strategy));
+            }
+        }
+    }
+
+    private static final class ShortArraySerializer extends ArraySerializer {
+
+        ShortArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            short[] array = (short[]) value;
+            for (short s : array) {
+                getValueSerializer().serialize(s, generator, context);
+            }
+        }
+
+    }
+
+    private static final class IntegerArraySerializer extends ArraySerializer {
+
+        IntegerArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            int[] array = (int[]) value;
+            for (int i : array) {
+                getValueSerializer().serialize(i, generator, context);
+            }
+        }
+
+    }
+
+    private static final class LongArraySerializer extends ArraySerializer {
+
+        LongArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            long[] array = (long[]) value;
+            for (long l : array) {
+                getValueSerializer().serialize(l, generator, context);
+            }
+        }
+
+    }
+
+    private static final class FloatArraySerializer extends ArraySerializer {
+
+        FloatArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            float[] array = (float[]) value;
+            for (float f : array) {
+                getValueSerializer().serialize(f, generator, context);
+            }
+        }
+
+    }
+
+    private static final class DoubleArraySerializer extends ArraySerializer {
+
+        DoubleArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            double[] array = (double[]) value;
+            for (double d : array) {
+                getValueSerializer().serialize(d, generator, context);
+            }
+        }
+
+    }
+
+    private static final class BooleanArraySerializer extends ArraySerializer {
+
+        BooleanArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            boolean[] array = (boolean[]) value;
+            for (boolean b : array) {
+                getValueSerializer().serialize(b, generator, context);
+            }
+        }
+
+    }
+
+    private static final class CharacterArraySerializer extends ArraySerializer {
+
+        CharacterArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            char[] array = (char[]) value;
+            for (char c : array) {
+                getValueSerializer().serialize(c, generator, context);
+            }
+        }
+
+    }
+
+    private static final class ObjectArraySerializer extends ArraySerializer {
+
+        ObjectArraySerializer(ModelSerializer valueSerializer) {
+            super(valueSerializer);
+        }
+
+        @Override
+        public void serializeArray(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            Object[] array = (Object[]) value;
+            for (Object o : array) {
+                getValueSerializer().serialize(o, generator, context);
+            }
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/CollectionSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/CollectionSerializer.java
new file mode 100644
index 0000000..2fa904a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/CollectionSerializer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.util.Collection;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Collection container serializer.
+ */
+class CollectionSerializer implements ModelSerializer {
+
+    private final ModelSerializer delegate;
+
+    CollectionSerializer(ModelSerializer delegate) {
+        this.delegate = delegate;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        Collection<Object> collection = (Collection<Object>) value;
+        generator.writeStartArray();
+        collection.forEach(object -> delegate.serialize(object, generator, context));
+        generator.writeEnd();
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/CyclicReferenceSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/CyclicReferenceSerializer.java
new file mode 100644
index 0000000..9102256
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/CyclicReferenceSerializer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Solution for cyclic references in serialization.
+ * This approach helps us to avoid creation of multiple serializers for the same type.
+ */
+class CyclicReferenceSerializer implements ModelSerializer {
+
+    private final Type type;
+    private ModelSerializer delegate;
+
+    CyclicReferenceSerializer(Type type) {
+        this.type = type;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        if (delegate == null) {
+            delegate = context.getJsonbContext().getSerializationModelCreator().serializerChain(type, true, true);
+        }
+        delegate.serialize(value, generator, context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/KeyWriter.java b/src/main/java/org/eclipse/yasson/internal/serializer/KeyWriter.java
new file mode 100644
index 0000000..d359f65
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/KeyWriter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Key name writer. Writes key name of the property if present.
+ */
+public class KeyWriter implements ModelSerializer {
+
+    private final ModelSerializer delegate;
+
+    /**
+     * Create new instance.
+     *
+     * @param delegate delegate to be called after the key is written
+     */
+    public KeyWriter(ModelSerializer delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        if (context.getKey() != null) {
+            generator.writeKey(context.getKey());
+            context.setKey(null);
+        }
+        delegate.serialize(value, generator, context);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/MapSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/MapSerializer.java
new file mode 100644
index 0000000..9b283fb
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/MapSerializer.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.util.Map;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.serializer.types.TypeSerializers;
+
+/**
+ * Map container serializer.
+ */
+abstract class MapSerializer implements ModelSerializer {
+
+    private final ModelSerializer keySerializer;
+    private final ModelSerializer valueSerializer;
+
+    MapSerializer(ModelSerializer keySerializer, ModelSerializer valueSerializer) {
+        this.keySerializer = keySerializer;
+        this.valueSerializer = valueSerializer;
+    }
+
+    ModelSerializer getKeySerializer() {
+        return keySerializer;
+    }
+
+    ModelSerializer getValueSerializer() {
+        return valueSerializer;
+    }
+
+    static MapSerializer create(Class<?> keyClass, ModelSerializer keySerializer, ModelSerializer valueSerializer) {
+        if (TypeSerializers.isSupportedMapKey(keyClass)) {
+            return new StringKeyMapSerializer(keySerializer, valueSerializer);
+        } else if (Object.class.equals(keyClass)) {
+            return new DynamicMapSerializer(keySerializer, valueSerializer);
+        }
+        return new ObjectKeyMapSerializer(keySerializer, valueSerializer);
+    }
+
+    private static final class DynamicMapSerializer extends MapSerializer {
+
+        private final StringKeyMapSerializer stringMap;
+        private final ObjectKeyMapSerializer objectMap;
+        private MapSerializer serializer;
+
+        DynamicMapSerializer(ModelSerializer keySerializer,
+                                    ModelSerializer valueSerializer) {
+            super(keySerializer, valueSerializer);
+            stringMap = new StringKeyMapSerializer(keySerializer, valueSerializer);
+            objectMap = new ObjectKeyMapSerializer(keySerializer, valueSerializer);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            if (serializer == null) {
+                //We have to be sure that Map with Object as a key contains only supported values for key:value format map.
+                Map<Object, Object> map = (Map<Object, Object>) value;
+                boolean suitable = true;
+                for (Object key : map.keySet()) {
+                    if (key == null) {
+                        if (context.getJsonbContext().getConfigProperties().isForceMapArraySerializerForNullKeys()) {
+                            suitable = false;
+                            break;
+                        }
+                        continue;
+                    }
+                    Class<?> keyClass = key.getClass();
+                    if (TypeSerializers.isSupportedMapKey(keyClass)) {
+                        continue;
+                    }
+                    //No other checks needed. Map is not suitable for normal key:value map. Wrapping object needs to be used.
+                    suitable = false;
+                    break;
+                }
+                serializer = suitable ? stringMap : objectMap;
+            }
+            serializer.serialize(value, generator, context);
+        }
+
+    }
+
+    private static final class StringKeyMapSerializer extends MapSerializer {
+
+        StringKeyMapSerializer(ModelSerializer keySerializer,
+                                      ModelSerializer valueSerializer) {
+            super(keySerializer, valueSerializer);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            Map<Object, Object> map = (Map<Object, Object>) value;
+            generator.writeStartObject();
+            map.forEach((key, val) -> {
+                getKeySerializer().serialize(key, generator, context);
+                getValueSerializer().serialize(val, generator, context);
+            });
+            generator.writeEnd();
+        }
+
+    }
+
+    private static final class ObjectKeyMapSerializer extends MapSerializer {
+
+        ObjectKeyMapSerializer(ModelSerializer keySerializer,
+                                      ModelSerializer valueSerializer) {
+            super(keySerializer, valueSerializer);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            Map<Object, Object> map = (Map<Object, Object>) value;
+            generator.writeStartArray();
+            map.forEach((key, val) -> {
+                generator.writeStartObject();
+                generator.writeKey("key");
+                if (key == null) {
+                    generator.writeNull();
+                } else {
+                    getKeySerializer().serialize(key, generator, context);
+                }
+                generator.writeKey("value");
+                getValueSerializer().serialize(val, generator, context);
+                generator.writeEnd();
+            });
+            generator.writeEnd();
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/ModelSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/ModelSerializer.java
new file mode 100644
index 0000000..60ea4a6
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/ModelSerializer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Type serializer.
+ * <br>
+ * All the instances are required to be reusable and without any states
+ * stored in the class fields.
+ */
+public interface ModelSerializer {
+
+    /**
+     * Serialize provided value or delegate serialization to the next serializer.
+     *
+     * @param value     value to be serialized
+     * @param generator json generator
+     * @param context   serialization context
+     */
+    void serialize(Object value, JsonGenerator generator, SerializationContextImpl context);
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/NullSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/NullSerializer.java
new file mode 100644
index 0000000..a94427b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/NullSerializer.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.model.customization.Customization;
+
+/**
+ * Null value serializer. Determines proper behavior when the serialized value is null.
+ */
+public class NullSerializer implements ModelSerializer {
+
+    private final ModelSerializer delegate;
+    private final ModelSerializer nullSerializer;
+    private final ModelSerializer rootNullSerializer;
+
+    /**
+     * Create new instance.
+     *
+     * @param delegate      non-null value delegate
+     * @param customization component customization
+     * @param jsonbContext  jsonb context
+     */
+    public NullSerializer(ModelSerializer delegate,
+                          Customization customization,
+                          JsonbContext jsonbContext) {
+        this.delegate = delegate;
+        if (customization.isNillable()) {
+            nullSerializer = new NullWritingEnabled();
+        } else {
+            nullSerializer = new NullWritingDisabled();
+        }
+        JsonbSerializer<?> userDefinedNullSerializer = jsonbContext.getConfigProperties().getNullSerializer();
+        if (userDefinedNullSerializer != null) {
+            rootNullSerializer = (value, generator, context) -> userDefinedNullSerializer.serialize(null, generator, context);
+        } else {
+            rootNullSerializer = nullSerializer;
+        }
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        if (value == null) {
+            if (context.isRoot()) {
+                context.setRoot(false);
+                rootNullSerializer.serialize(null, generator, context);
+            } else {
+                nullSerializer.serialize(null, generator, context);
+            }
+            context.setKey(null);
+        } else {
+            context.setRoot(false);
+            delegate.serialize(value, generator, context);
+        }
+    }
+
+    private static final class NullWritingEnabled implements ModelSerializer {
+
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            if (context.getKey() == null) {
+                generator.writeNull();
+            } else {
+                generator.writeNull(context.getKey());
+            }
+        }
+
+    }
+
+    private static class NullWritingDisabled implements ModelSerializer {
+
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            if (context.isContainerWithNulls()) {
+                if (context.getKey() == null) {
+                    generator.writeNull();
+                } else {
+                    generator.writeNull(context.getKey());
+                }
+            }
+            context.setKey(null);
+            //Do nothing
+        }
+
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/NullVisibilitySwitcher.java b/src/main/java/org/eclipse/yasson/internal/serializer/NullVisibilitySwitcher.java
new file mode 100644
index 0000000..618961a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/NullVisibilitySwitcher.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Switching mechanism for default null value visibility in the JSON.
+ *
+ * Some constructs such as arrays, collections etc. require to have nulls serialized into the JSON by default.
+ * This class switches from the default parent null visibility to the current construct visibility. As soon as the current
+ * construct is serialized, visibility is switched back to the parent ones.
+ */
+class NullVisibilitySwitcher implements ModelSerializer {
+
+    private final boolean nullsEnabled;
+    private final ModelSerializer delegate;
+
+    NullVisibilitySwitcher(boolean nullsEnabled, ModelSerializer delegate) {
+        this.nullsEnabled = nullsEnabled;
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        boolean previous = context.isContainerWithNulls();
+        context.setContainerWithNulls(nullsEnabled);
+        delegate.serialize(value, generator, context);
+        context.setContainerWithNulls(previous);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java
new file mode 100644
index 0000000..336a782
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.util.LinkedHashMap;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Object container serializer.
+ */
+class ObjectSerializer implements ModelSerializer {
+
+    private final LinkedHashMap<String, ModelSerializer> propertySerializers;
+
+    ObjectSerializer(LinkedHashMap<String, ModelSerializer> propertySerializers) {
+        this.propertySerializers = propertySerializers;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.writeStartObject();
+        propertySerializers.forEach((key, serializer) -> {
+            try {
+                context.setKey(key);
+                serializer.serialize(value, generator, context);
+            } catch (Exception e) {
+                throw new JsonbException(Messages.getMessage(MessageKeys.SERIALIZE_PROPERTY_ERROR, key,
+                                                             value.getClass().getCanonicalName()), e);
+            }
+        });
+        generator.writeEnd();
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/OptionalSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/OptionalSerializer.java
new file mode 100644
index 0000000..27e50d0
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/OptionalSerializer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.util.Optional;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Optional container serializer.
+ */
+class OptionalSerializer implements ModelSerializer {
+
+    private final ModelSerializer delegate;
+
+    OptionalSerializer(ModelSerializer delegate) {
+        this.delegate = delegate;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        Optional<Object> optional = (Optional<Object>) value;
+        delegate.serialize(optional == null ? null : optional.orElse(null), generator, context);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/RecursionChecker.java b/src/main/java/org/eclipse/yasson/internal/serializer/RecursionChecker.java
new file mode 100644
index 0000000..32d746c
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/RecursionChecker.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Recursion checker serializer deals with possible instance recursion in instances.
+ */
+class RecursionChecker implements ModelSerializer {
+
+    private final ModelSerializer delegate;
+
+    RecursionChecker(ModelSerializer delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        if (!context.addProcessedObject(value)) {
+            throw new JsonbException(Messages.getMessage(MessageKeys.RECURSIVE_REFERENCE, value.getClass()));
+        }
+        delegate.serialize(value, generator, context);
+        context.removeProcessedObject(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java b/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java
new file mode 100644
index 0000000..522519b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/SerializationModelCreator.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2021, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.ComponentMatcher;
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.ReflectionUtils;
+import org.eclipse.yasson.internal.components.AdapterBinding;
+import org.eclipse.yasson.internal.components.SerializerBinding;
+import org.eclipse.yasson.internal.model.ClassModel;
+import org.eclipse.yasson.internal.model.PropertyModel;
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+import org.eclipse.yasson.internal.model.customization.ComponentBoundCustomization;
+import org.eclipse.yasson.internal.model.customization.Customization;
+import org.eclipse.yasson.internal.model.customization.TypeInheritanceConfiguration;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+import org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer;
+import org.eclipse.yasson.internal.serializer.types.TypeSerializers;
+
+/**
+ * Create or obtain already created type serializer.
+ */
+public class SerializationModelCreator {
+
+    private final Map<Type, ModelSerializer> explicitChain = new ConcurrentHashMap<>();
+    private final Map<Type, ModelSerializer> dynamicChain = new ConcurrentHashMap<>();
+    private final JsonbContext jsonbContext;
+
+    /**
+     * Create new instance.
+     *
+     * @param jsonbContext jsonb context
+     */
+    public SerializationModelCreator(JsonbContext jsonbContext) {
+        this.jsonbContext = jsonbContext;
+    }
+
+    /**
+     * Wrap {@link ModelSerializer} in the common set of serializers.
+     *
+     * @param modelSerializer serializer to be wrapped
+     * @param customization   component customization
+     * @param jsonbContext    jsonb context
+     * @return wrapped serializer
+     */
+    public static ModelSerializer wrapInCommonSet(ModelSerializer modelSerializer,
+                                                  Customization customization,
+                                                  JsonbContext jsonbContext) {
+        KeyWriter serializer = new KeyWriter(modelSerializer);
+        return new NullSerializer(serializer, customization, jsonbContext);
+    }
+
+    /**
+     * Create new {@link ModelSerializer} of the given type.
+     *
+     * @param type               type to be serialized
+     * @param rootValue          whether it is a root value
+     * @param resolveRootAdapter whether to resolve root adapter
+     * @return type model serializer
+     */
+    public ModelSerializer serializerChain(Type type, boolean rootValue, boolean resolveRootAdapter) {
+        Class<?> rawType = ReflectionUtils.getRawType(type);
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawType);
+        LinkedList<Type> chain = new LinkedList<>();
+        return serializerChain(chain, type, classModel.getClassCustomization(), rootValue, false, resolveRootAdapter);
+    }
+
+    /**
+     * Create new {@link ModelSerializer} of the given type.
+     *
+     * @param chain                 chain of types used before the one currently processed
+     * @param type                  type to be serialized
+     * @param propertyCustomization component customization
+     * @param rootValue             whether it is a root value
+     * @param isKey                 whether it is a key
+     * @return type model serializer
+     */
+    public ModelSerializer serializerChainRuntime(LinkedList<Type> chain,
+                                                  Type type,
+                                                  Customization propertyCustomization,
+                                                  boolean rootValue,
+                                                  boolean isKey) {
+        if (chain.contains(type)) {
+            return new CyclicReferenceSerializer(type);
+        }
+        //If the class instance and class of the field are the same and there has been generics specified for this field,
+        //we need to use those instead of raw type.
+        Class<?> rawType = ReflectionUtils.getRawType(type);
+        Class<?> rawLast = ReflectionUtils.getRawType(chain.getLast());
+        if (rawLast.equals(rawType)) {
+            return serializerChainInternal(chain, chain.getLast(), propertyCustomization, rootValue, isKey, true);
+        }
+        return serializerChainInternal(chain, type, propertyCustomization, rootValue, isKey, true);
+    }
+
+    private ModelSerializer serializerChain(LinkedList<Type> chain,
+                                            Type type,
+                                            Customization propertyCustomization,
+                                            boolean rootValue,
+                                            boolean isKey,
+                                            boolean resolveRootAdapter) {
+        if (chain.contains(type)) {
+            return new CyclicReferenceSerializer(type);
+        }
+        try {
+            chain.add(type);
+            return serializerChainInternal(chain, type, propertyCustomization, rootValue, isKey, resolveRootAdapter);
+        } finally {
+            chain.removeLast();
+        }
+    }
+
+    private ModelSerializer serializerChainInternal(LinkedList<Type> chain,
+                                                    Type type,
+                                                    Customization propertyCustomization,
+                                                    boolean rootValue,
+                                                    boolean isKey,
+                                                    boolean resolveRootAdapter) {
+        if (explicitChain.containsKey(type)) {
+            return explicitChain.get(type);
+        }
+        Class<?> rawType = ReflectionUtils.getRawType(type);
+        Optional<ModelSerializer> serializerBinding = userSerializer(type,
+                                                                     (ComponentBoundCustomization) propertyCustomization);
+        if (serializerBinding.isPresent()) {
+            return serializerBinding.get();
+        }
+        if (resolveRootAdapter) {
+            Optional<AdapterBinding> maybeAdapter = adapterBinding(type, (ComponentBoundCustomization) propertyCustomization);
+            if (maybeAdapter.isPresent()) {
+                AdapterBinding adapterBinding = maybeAdapter.get();
+                Type toType = adapterBinding.getToType();
+                Class<?> rawToType = ReflectionUtils.getRawType(toType);
+                ModelSerializer typeSerializer = TypeSerializers
+                        .getTypeSerializer(rawToType, propertyCustomization, jsonbContext);
+                if (typeSerializer == null) {
+                    typeSerializer = serializerChain(toType, rootValue, !type.equals(toType));
+                }
+                AdapterSerializer adapterSerializer = new AdapterSerializer(adapterBinding, typeSerializer);
+                RecursionChecker recursionChecker = new RecursionChecker(adapterSerializer);
+                NullSerializer nullSerializer = new NullSerializer(recursionChecker, propertyCustomization, jsonbContext);
+                explicitChain.put(type, nullSerializer);
+                return nullSerializer;
+            }
+        }
+
+        ModelSerializer typeSerializer = null;
+        if (!Object.class.equals(rawType)) {
+            typeSerializer = TypeSerializers.getTypeSerializer(chain, rawType, propertyCustomization, jsonbContext, isKey);
+        }
+        if (typeSerializer != null) {
+            if (jsonbContext.getConfigProperties().isStrictIJson() && rootValue) {
+                throw new JsonbException(Messages.getMessage(MessageKeys.IJSON_ENABLED_SINGLE_VALUE));
+            }
+            return typeSerializer;
+        }
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawType);
+        if (Collection.class.isAssignableFrom(rawType)) {
+            return createCollectionSerializer(chain, type, propertyCustomization);
+        } else if (Map.class.isAssignableFrom(rawType)) {
+            return createMapSerializer(chain, type, propertyCustomization);
+        } else if (rawType.isArray()) {
+            return createArraySerializer(chain, rawType, propertyCustomization);
+        } else if (type instanceof GenericArrayType) {
+            return createGenericArraySerializer(chain, type, propertyCustomization);
+        } else if (Optional.class.equals(rawType)) {
+            return createOptionalSerializer(chain, type, propertyCustomization, isKey);
+        }
+        return createObjectSerializer(chain, type, classModel);
+    }
+
+    private ModelSerializer createObjectSerializer(LinkedList<Type> chain,
+                                                   Type type,
+                                                   ClassModel classModel) {
+        LinkedHashMap<String, ModelSerializer> propertySerializers = new LinkedHashMap<>();
+        TypeInheritanceConfiguration typeInheritanceConfiguration = classModel.getClassCustomization().getPolymorphismConfig();
+        if (typeInheritanceConfiguration != null) {
+            addPolymorphismProperty(typeInheritanceConfiguration, propertySerializers, classModel);
+        }
+        for (PropertyModel model : classModel.getSortedProperties()) {
+            if (model.isReadable()) {
+                String name = model.getWriteName();
+                ModelSerializer memberModel = memberSerializer(chain,
+                                                               model.getPropertySerializationType(),
+                                                               model.getCustomization(),
+                                                               false);
+                propertySerializers.put(name, new ValueGetterSerializer(model.getGetValueHandle(), memberModel));
+            }
+        }
+        ModelSerializer objectSerializer = new ObjectSerializer(propertySerializers);
+        RecursionChecker recursionChecker = new RecursionChecker(objectSerializer);
+        KeyWriter keyWriter = new KeyWriter(recursionChecker);
+        NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(false, keyWriter);
+        NullSerializer nullSerializer = new NullSerializer(nullVisibilitySwitcher, classModel.getClassCustomization(),
+                                                           jsonbContext);
+        explicitChain.put(type, nullSerializer);
+        return nullSerializer;
+    }
+
+    private void addPolymorphismProperty(TypeInheritanceConfiguration typeInheritanceConfiguration,
+                                         LinkedHashMap<String, ModelSerializer> propertySerializers,
+                                         ClassModel classModel) {
+        Class<?> rawType = classModel.getType();
+        String alias = typeInheritanceConfiguration.getAliases().get(rawType);
+        ModelSerializer serializer = createPolymorphismPropertySerializer(typeInheritanceConfiguration, alias);
+        if ((!typeInheritanceConfiguration.isInherited() || alias != null) && typeInheritanceConfiguration.getParentConfig() != null) {
+            addParentPolymorphismProperty(typeInheritanceConfiguration.getParentConfig(), propertySerializers, classModel);
+        }
+        if (serializer != null) {
+            propertySerializers.put(typeInheritanceConfiguration.getFieldName(), serializer);
+        }
+        for (PropertyModel propertyModel : classModel.getSortedProperties()) {
+            if (propertySerializers.containsKey(propertyModel.getWriteName())) {
+                throw new JsonbException("CHANGE naming conflict!");
+            }
+        }
+    }
+
+    private void addParentPolymorphismProperty(TypeInheritanceConfiguration typeInheritanceConfiguration,
+                                               LinkedHashMap<String, ModelSerializer> propertySerializers,
+                                               ClassModel classModel) {
+        Class<?> rawType = classModel.getType();
+        TypeInheritanceConfiguration current = typeInheritanceConfiguration;
+        LinkedHashMap<String, ModelSerializer> toBeAdded = new LinkedHashMap<>();
+        while (current != null) {
+            TypeInheritanceConfiguration local = current;
+            String alias = local.getAliases().entrySet().stream()
+                    .filter(entry -> entry.getKey().isAssignableFrom(rawType))
+                    .map(Map.Entry::getValue)
+                    .findFirst()
+                    .orElse(null);
+            if (alias != null) {
+                ModelSerializer serializer = createPolymorphismPropertySerializer(local, alias);
+                toBeAdded.put(current.getFieldName(), serializer);
+                current = current.getParentConfig();
+            }
+        }
+        ListIterator<Map.Entry<String, ModelSerializer>> iterator = new ArrayList<>(toBeAdded.entrySet())
+                .listIterator(toBeAdded.size());
+        while (iterator.hasPrevious()) {
+            Map.Entry<String, ModelSerializer> entry = iterator.previous();
+            propertySerializers.put(entry.getKey(), entry.getValue());
+        }
+    }
+
+    private ModelSerializer createPolymorphismPropertySerializer(TypeInheritanceConfiguration configuration, String alias) {
+        if (alias != null) {
+            return (value, generator, context) -> generator.write(configuration.getFieldName(), alias);
+        }
+        return null;
+    }
+
+    private ModelSerializer createCollectionSerializer(LinkedList<Type> chain,
+                                                       Type type,
+                                                       Customization customization) {
+        Type colType = type instanceof ParameterizedType
+                ? ((ParameterizedType) type).getActualTypeArguments()[0]
+                : Object.class;
+        Type resolvedKey = ReflectionUtils.resolveType(chain, colType);
+        Class<?> rawClass = ReflectionUtils.getRawType(resolvedKey);
+        ClassModel classModel = jsonbContext.getMappingContext().getOrCreateClassModel(rawClass);
+        ModelSerializer typeSerializer = memberSerializer(chain, colType, classModel.getClassCustomization(), false);
+        CollectionSerializer collectionSerializer = new CollectionSerializer(typeSerializer);
+        KeyWriter keyWriter = new KeyWriter(collectionSerializer);
+        NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter);
+        return new NullSerializer(nullVisibilitySwitcher, customization, jsonbContext);
+    }
+
+    private ModelSerializer createMapSerializer(LinkedList<Type> chain, Type type, Customization propertyCustomization) {
+        Type keyType = type instanceof ParameterizedType
+                ? ((ParameterizedType) type).getActualTypeArguments()[0]
+                : Object.class;
+        Type valueType = type instanceof ParameterizedType
+                ? ((ParameterizedType) type).getActualTypeArguments()[1]
+                : Object.class;
+        Type resolvedKey = ReflectionUtils.resolveType(chain, keyType);
+        Class<?> rawClass = ReflectionUtils.getRawType(resolvedKey);
+        ModelSerializer keySerializer = memberSerializer(chain, keyType, ClassCustomization.empty(), true);
+        ModelSerializer valueSerializer = memberSerializer(chain, valueType, propertyCustomization, false);
+        MapSerializer mapSerializer = MapSerializer.create(rawClass, keySerializer, valueSerializer);
+        KeyWriter keyWriter = new KeyWriter(mapSerializer);
+        NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter);
+        return new NullSerializer(nullVisibilitySwitcher, propertyCustomization, jsonbContext);
+    }
+
+    private ModelSerializer createArraySerializer(LinkedList<Type> chain,
+                                                  Class<?> raw,
+                                                  Customization propertyCustomization) {
+        Class<?> arrayComponent = raw.getComponentType();
+        ModelSerializer modelSerializer = memberSerializer(chain, arrayComponent, propertyCustomization, false);
+        ModelSerializer arraySerializer = ArraySerializer.create(raw, jsonbContext, modelSerializer);
+        KeyWriter keyWriter = new KeyWriter(arraySerializer);
+        NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter);
+        return new NullSerializer(nullVisibilitySwitcher, propertyCustomization, jsonbContext);
+    }
+
+    private ModelSerializer createGenericArraySerializer(LinkedList<Type> chain,
+                                                         Type type,
+                                                         Customization propertyCustomization) {
+        Class<?> raw = ReflectionUtils.getRawType(type);
+        Class<?> component = ReflectionUtils.getRawType(((GenericArrayType) type).getGenericComponentType());
+        ModelSerializer modelSerializer = memberSerializer(chain, component, propertyCustomization, false);
+        ModelSerializer arraySerializer = ArraySerializer.create(raw, jsonbContext, modelSerializer);
+        KeyWriter keyWriter = new KeyWriter(arraySerializer);
+        NullVisibilitySwitcher nullVisibilitySwitcher = new NullVisibilitySwitcher(true, keyWriter);
+        return new NullSerializer(nullVisibilitySwitcher, propertyCustomization, jsonbContext);
+    }
+
+    private ModelSerializer createOptionalSerializer(LinkedList<Type> chain,
+                                                     Type type,
+                                                     Customization propertyCustomization,
+                                                     boolean isKey) {
+        Type optType = type instanceof ParameterizedType
+                ? ((ParameterizedType) type).getActualTypeArguments()[0]
+                : Object.class;
+        ModelSerializer modelSerializer = memberSerializer(chain, optType, propertyCustomization, isKey);
+        return new OptionalSerializer(modelSerializer);
+    }
+
+    private ModelSerializer memberSerializer(LinkedList<Type> chain,
+                                             Type type,
+                                             Customization customization,
+                                             boolean key) {
+        Type resolved = ReflectionUtils.resolveType(chain, type);
+        Class<?> rawType = ReflectionUtils.getRawType(resolved);
+
+        Optional<ModelSerializer> serializerBinding = userSerializer(resolved,
+                                                                     (ComponentBoundCustomization) customization);
+        if (serializerBinding.isPresent()) {
+            return serializerBinding.get();
+        }
+        Optional<AdapterBinding> maybeAdapter = adapterBinding(resolved, (ComponentBoundCustomization) customization);
+        if (maybeAdapter.isPresent()) {
+            AdapterBinding adapterBinding = maybeAdapter.get();
+            Type toType = adapterBinding.getToType();
+            Class<?> rawToType = ReflectionUtils.getRawType(toType);
+            ModelSerializer typeSerializer = TypeSerializers.getTypeSerializer(rawToType, customization, jsonbContext);
+            if (typeSerializer == null) {
+                typeSerializer = serializerChain(toType, false, true);
+            }
+            AdapterSerializer adapterSerializer = new AdapterSerializer(adapterBinding, typeSerializer);
+            return new NullSerializer(adapterSerializer, customization, jsonbContext);
+        }
+        ModelSerializer typeSerializer = TypeSerializers.getTypeSerializer(chain, rawType, customization, jsonbContext, key);
+        if (typeSerializer == null) {
+            //Final classes dont have any child classes. It is safe to assume that there will be instance of that specific class.
+            boolean isFinal = Modifier.isFinal(rawType.getModifiers());
+            if (isFinal
+                    || Collection.class.isAssignableFrom(rawType)
+                    || Map.class.isAssignableFrom(rawType)) {
+                return serializerChain(chain, resolved, customization, false, key, true);
+            } else {
+                if (dynamicChain.containsKey(resolved)) {
+                    return dynamicChain.get(resolved);
+                }
+                boolean isAbstract = Modifier.isAbstract(rawType.getModifiers());
+                ModelSerializer specificTypeSerializer = null;
+                if (!isAbstract && !rawType.equals(Object.class)) {
+                    if (explicitChain.containsKey(resolved)) {
+                        specificTypeSerializer = explicitChain.get(resolved);
+                    } else {
+                        specificTypeSerializer = serializerChain(chain, resolved, customization, false, key, true);
+                    }
+                }
+                //Needs to be dynamically resolved with special cache since possible inheritance problem.
+                if (resolved instanceof Class) {
+                    typeSerializer = TypeSerializers.getTypeSerializer(chain, Object.class, customization, jsonbContext, key);
+                } else {
+                    chain.add(resolved);
+                    typeSerializer = TypeSerializers.getTypeSerializer(chain, Object.class, customization, jsonbContext, key);
+                    chain.removeLast();
+                }
+                if (specificTypeSerializer != null && typeSerializer instanceof ObjectTypeSerializer) {
+                    ((ObjectTypeSerializer) typeSerializer).addSpecificSerializer(rawType, specificTypeSerializer);
+                }
+                //Since typeSerializer is handled as Object currently, we need to wrap it with null checker (if it is not a key)
+                if (!key) {
+                    typeSerializer = new NullSerializer(typeSerializer, customization, jsonbContext);
+                }
+
+                dynamicChain.put(type, typeSerializer);
+            }
+        }
+        if (!key && typeSerializer instanceof ObjectTypeSerializer) {
+            typeSerializer = new NullSerializer(typeSerializer, customization, jsonbContext);
+        }
+        return typeSerializer;
+    }
+
+    private Optional<ModelSerializer> userSerializer(Type type, ComponentBoundCustomization classCustomization) {
+        final ComponentMatcher componentMatcher = jsonbContext.getComponentMatcher();
+        return componentMatcher.getSerializerBinding(type, classCustomization)
+                .map(SerializerBinding::getJsonbSerializer)
+                .map(UserDefinedSerializer::new)
+                .map(RecursionChecker::new)
+                .map(serializer -> SerializationModelCreator.wrapInCommonSet(serializer,
+                                                                             (Customization) classCustomization,
+                                                                             jsonbContext));
+    }
+
+    private Optional<AdapterBinding> adapterBinding(Type type, ComponentBoundCustomization classCustomization) {
+        return jsonbContext.getComponentMatcher().getSerializeAdapterBinding(type, classCustomization);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/SerializerBuilderParams.java b/src/main/java/org/eclipse/yasson/internal/serializer/SerializerBuilderParams.java
new file mode 100644
index 0000000..525d4d2
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/SerializerBuilderParams.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+import org.eclipse.yasson.internal.model.customization.Customization;
+
+/**
+ * Not currently supported. Possibly implemented in the future.
+ *
+ * Holder of serialization parameters during creation process. Reduces the number of needed parameters.
+ */
+class SerializerBuilderParams {
+
+    private final Type type;
+    private final Customization customization;
+    private final boolean root;
+    private final boolean key;
+    private final boolean resolveRootAdapter;
+    private final ModelSerializer objectBaseSerializer;
+
+    private SerializerBuilderParams(Builder builder) {
+        this.type = builder.type;
+        this.customization = builder.customization;
+        this.root = builder.root;
+        this.key = builder.key;
+        this.resolveRootAdapter = builder.resolveRootAdapter;
+        this.objectBaseSerializer = builder.objectBaseSerializer;
+    }
+
+    public static Builder builder(Type type) {
+        return new Builder(type);
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public Customization getCustomization() {
+        return customization;
+    }
+
+    public boolean isRoot() {
+        return root;
+    }
+
+    public boolean isKey() {
+        return key;
+    }
+
+    public boolean isResolveRootAdapter() {
+        return resolveRootAdapter;
+    }
+
+    public ModelSerializer getObjectBaseSerializer() {
+        return objectBaseSerializer;
+    }
+
+    static final class Builder {
+
+        private Type type;
+        private Customization customization;
+        private boolean root;
+        private boolean key;
+        private boolean resolveRootAdapter;
+        private ModelSerializer objectBaseSerializer;
+
+        private Builder(Type type) {
+            this.type = Objects.requireNonNull(type);
+            this.customization = ClassCustomization.empty();
+            this.root = true;
+            this.key = false;
+        }
+
+        public Builder type(Type type) {
+            this.type = Objects.requireNonNull(type);
+            return this;
+        }
+
+        public Builder customization(Customization customization) {
+            this.customization = Objects.requireNonNull(customization);
+            return this;
+        }
+
+        public Builder root(boolean root) {
+            this.root = root;
+            return this;
+        }
+
+        public Builder key(boolean key) {
+            this.key = key;
+            return this;
+        }
+
+        public Builder resolveRootAdapter(boolean resolveRootAdapter) {
+            this.resolveRootAdapter = resolveRootAdapter;
+            return this;
+        }
+
+        public Builder objectBaseSerializer(ModelSerializer objectBaseSerializer) {
+            this.objectBaseSerializer = objectBaseSerializer;
+            return this;
+        }
+
+        public SerializerBuilderParams build() {
+            return new SerializerBuilderParams(this);
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/UserDefinedSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/UserDefinedSerializer.java
new file mode 100644
index 0000000..88edd11
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/UserDefinedSerializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * User defined serializer executor.
+ */
+class UserDefinedSerializer<T> implements ModelSerializer {
+
+    private final JsonbSerializer<T> userDefinedSerializer;
+
+    UserDefinedSerializer(JsonbSerializer<T> userDefinedSerializer) {
+        this.userDefinedSerializer = userDefinedSerializer;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        YassonGenerator yassonGenerator = new YassonGenerator(generator);
+        userDefinedSerializer.serialize((T) value, yassonGenerator, context);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/ValueGetterSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/ValueGetterSerializer.java
new file mode 100644
index 0000000..d22c1a4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/ValueGetterSerializer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.lang.invoke.MethodHandle;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Extractor of the serialized value from the instance.
+ */
+class ValueGetterSerializer implements ModelSerializer {
+
+    private final MethodHandle valueGetter;
+    private final ModelSerializer delegate;
+
+    ValueGetterSerializer(MethodHandle valueGetter, ModelSerializer delegate) {
+        this.valueGetter = valueGetter;
+        this.delegate = delegate;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        Object object;
+        try {
+            object = valueGetter.invoke(value);
+        } catch (Throwable e) {
+            throw new JsonbException("Error getting value on: " + value.getClass().getName(), e);
+        }
+        delegate.serialize(object, generator, context);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/YassonGenerator.java b/src/main/java/org/eclipse/yasson/internal/serializer/YassonGenerator.java
new file mode 100644
index 0000000..1709e33
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/YassonGenerator.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Yasson {@link JsonGenerator} generator wrapper.
+ * <br>
+ * Used for user defined serializers. Does not allow serializer to write outside the scope it should be used on.
+ */
+class YassonGenerator implements JsonGenerator {
+
+    private final JsonGenerator delegate;
+    private int level;
+
+    YassonGenerator(JsonGenerator delegate) {
+        this.delegate = delegate;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        writeValidate("writeStartObject()");
+        level++;
+        delegate.writeStartObject();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        writeValidate("writeStartObject(String name)");
+        level++;
+        delegate.writeStartObject(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeKey(String name) {
+        writeValidate("writeKey(String name)");
+        delegate.writeKey(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        writeValidate("writeStartArray()");
+        level++;
+        delegate.writeStartArray();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        writeValidate("writeStartArray(String name)");
+        level++;
+        delegate.writeStartArray(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        writeValidate("write(String name, JsonValue value)");
+        delegate.write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, String value) {
+        writeValidate("write(String name, String value)");
+        delegate.write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        writeValidate("write(String name, BigInteger value)");
+        delegate.write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        writeValidate("write(String name, BigDecimal value)");
+        delegate.write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        writeValidate("write(String name, int value)");
+        delegate.write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        writeValidate("write(String name, long value)");
+        delegate.write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        writeValidate("write(String name, double value)");
+        delegate.write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        writeValidate("write(String name, boolean value)");
+        delegate.write(name, value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        writeValidate("writeNull(String name)");
+        delegate.writeNull(name);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        level--;
+        if (level < 0) {
+            throw new JsonbException("writeEnd() cannot be called outside of the scope of user generator.");
+        }
+        if (level == 0) {
+            level--; //if user has closed array or object and is on the same level he started. There is no more allowed writing.
+        }
+        delegate.writeEnd();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        writeValidate("write(JsonValue value)");
+        delegate.write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(String value) {
+        writeValidate("write(String value)");
+        delegate.write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        writeValidate("write(BigDecimal value)");
+        delegate.write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        writeValidate("write(BigInteger value)");
+        delegate.write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(int value) {
+        writeValidate("write(int value)");
+        delegate.write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        writeValidate("write(long value)");
+        delegate.write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        writeValidate("write(double value)");
+        delegate.write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator write(boolean value) {
+        writeValidate("write(boolean value)");
+        delegate.write(value);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        writeValidate("writeNull()");
+        delegate.writeNull();
+        return this;
+    }
+
+    @Override
+    public void close() {
+        throw new JsonbException("Unsupported operation in user defined serializer.");
+    }
+
+    @Override
+    public void flush() {
+        throw new JsonbException("Unsupported operation in user defined serializer.");
+    }
+
+    private void writeValidate(String method) {
+        if (level < 0) {
+            throw new JsonbException(method + " cannot be called outside of the scope of user generator.");
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/AbstractDateSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/AbstractDateSerializer.java
new file mode 100644
index 0000000..1ff1df4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/AbstractDateSerializer.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.JsonbConfigProperties;
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.model.customization.Customization;
+
+/**
+ * Base for all date related serializers.
+ */
+abstract class AbstractDateSerializer<T> extends TypeSerializer<T> {
+
+    static final ZoneId UTC = ZoneId.of("UTC");
+
+    private final Function<T, String> toStringSerializer;
+    private final BiConsumer<T, JsonGenerator> valueWriter;
+
+    AbstractDateSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+        Customization customization = serializerBuilder.getCustomization();
+        JsonbConfigProperties properties = serializerBuilder.getJsonbContext().getConfigProperties();
+        final JsonbDateFormatter formatter = getJsonbDateFormatter(properties, customization);
+        toStringSerializer = valueSerializer(serializerBuilder);
+        if (JsonbDateFormat.TIME_IN_MILLIS.equals(formatter.getFormat()) && !properties.isDateInMillisecondsAsString()) {
+            valueWriter = (value, generator) -> generator.write(toInstant(value).toEpochMilli());
+        } else {
+            valueWriter = (value, generator) -> generator.write(toStringSerializer.apply(value));
+        }
+    }
+
+    private Function<T, String> valueSerializer(TypeSerializerBuilder serializerBuilder) {
+        Customization customization = serializerBuilder.getCustomization();
+        JsonbConfigProperties properties = serializerBuilder.getJsonbContext().getConfigProperties();
+        final JsonbDateFormatter formatter = getJsonbDateFormatter(properties, customization);
+        if (JsonbDateFormat.TIME_IN_MILLIS.equals(formatter.getFormat())) {
+            return value -> String.valueOf(toInstant(value).toEpochMilli());
+        } else if (formatter.getDateTimeFormatter() != null) {
+            DateTimeFormatter dateTimeFormatter = formatter.getDateTimeFormatter();
+            return value -> formatWithFormatter(value, dateTimeFormatter);
+        } else {
+            DateTimeFormatter configDateTimeFormatter = properties.getConfigDateFormatter().getDateTimeFormatter();
+            if (configDateTimeFormatter != null) {
+                return value -> formatWithFormatter(value, configDateTimeFormatter);
+            }
+        }
+        if (properties.isStrictIJson()) {
+            return this::formatStrictIJson;
+        }
+        Locale locale = properties.getLocale(formatter.getLocale());
+        return value -> formatDefault(value, locale);
+    }
+
+    private JsonbDateFormatter getJsonbDateFormatter(JsonbConfigProperties properties, Customization customization) {
+        return Optional.ofNullable(customization.getSerializeDateFormatter())
+                .orElse(properties.getConfigDateFormatter());
+    }
+
+    /**
+     * Convert date object to {@link TemporalAccessor}
+     *
+     * Only for legacy dates.
+     *
+     * @param value date object
+     * @return converted {@link TemporalAccessor}
+     */
+    protected TemporalAccessor toTemporalAccessor(T value) {
+        return (TemporalAccessor) value;
+    }
+
+    /**
+     * Convert java.time object to epoch milliseconds instant. Discards zone offset and zone id information.
+     *
+     * @param value date object to convert
+     * @return instant
+     */
+    protected abstract Instant toInstant(T value);
+
+    /**
+     * Format with default formatter for a given java.time date object.
+     * Different default formatter for each date object type is used.
+     *
+     * @param value  date object
+     * @param locale locale from annotation / default not null
+     * @return formatted date obj as string
+     */
+    protected abstract String formatDefault(T value, Locale locale);
+
+    /**
+     * Format date object with given formatter.
+     *
+     * @param value     date object to format
+     * @param formatter formatter to format with
+     * @return formatted result
+     */
+    protected String formatWithFormatter(T value, DateTimeFormatter formatter) {
+        return formatter.format(toTemporalAccessor(value));
+    }
+
+    /**
+     * Format date object as strict IJson date format.
+     *
+     * @param value value to format
+     * @return formatted result
+     */
+    protected String formatStrictIJson(T value) {
+        return JsonbDateFormatter.IJSON_DATE_FORMATTER.format(toTemporalAccessor(value));
+    }
+
+    /**
+     * Append UTC zone in case zone is not set on formatter.
+     *
+     * @param formatter formatter
+     * @return zoned formatter
+     */
+    protected DateTimeFormatter getZonedFormatter(DateTimeFormatter formatter) {
+        return formatter.getZone() != null
+                ? formatter
+                : formatter.withZone(UTC);
+    }
+
+    @Override
+    void serializeValue(T value, JsonGenerator generator, SerializationContextImpl context) {
+        valueWriter.accept(value, generator);
+    }
+
+    @Override
+    void serializeKey(T key, JsonGenerator generator, SerializationContextImpl context) {
+        generator.writeKey(toStringSerializer.apply(key));
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/AbstractNumberSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/AbstractNumberSerializer.java
new file mode 100644
index 0000000..e40d040
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/AbstractNumberSerializer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.JsonbNumberFormatter;
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.model.customization.Customization;
+import org.eclipse.yasson.internal.serializer.ModelSerializer;
+
+/**
+ * Base for all number related serializers.
+ */
+abstract class AbstractNumberSerializer<T> extends TypeSerializer<T> {
+
+    private final ModelSerializer actualSerializer;
+
+    AbstractNumberSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+        actualSerializer = actualSerializer(builder.getCustomization(), builder.getJsonbContext());
+    }
+
+    @SuppressWarnings("unchecked")
+    private ModelSerializer actualSerializer(Customization customization, JsonbContext jsonbContext) {
+        JsonbNumberFormatter formatter = customization.getSerializeNumberFormatter();
+        if (formatter == null) {
+            return (value, generator, context) -> writeValue((T) value, generator);
+        }
+        final NumberFormat format = NumberFormat
+                .getInstance(jsonbContext.getConfigProperties().getLocale(formatter.getLocale()));
+        ((DecimalFormat) format).applyPattern(formatter.getFormat());
+        return (value, generator, context) -> generator.write(format.format(value));
+    }
+
+    @Override
+    void serializeValue(T value, JsonGenerator generator, SerializationContextImpl context) {
+        actualSerializer.serialize(value, generator, context);
+    }
+
+    abstract void writeValue(T value, JsonGenerator generator);
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/BigDecimalSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/BigDecimalSerializer.java
new file mode 100644
index 0000000..740d9dd
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/BigDecimalSerializer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.math.BigDecimal;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link BigDecimal} type.
+ */
+class BigDecimalSerializer extends AbstractNumberSerializer<BigDecimal> {
+
+    BigDecimalSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(BigDecimal value, JsonGenerator generator) {
+        generator.write(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/BigIntegerSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/BigIntegerSerializer.java
new file mode 100644
index 0000000..f93c3f1
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/BigIntegerSerializer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.math.BigInteger;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link BigInteger} type.
+ */
+class BigIntegerSerializer extends AbstractNumberSerializer<BigInteger> {
+
+    BigIntegerSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(BigInteger value, JsonGenerator generator) {
+        generator.write(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/BooleanSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/BooleanSerializer.java
new file mode 100644
index 0000000..ff60782
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/BooleanSerializer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link Boolean} type.
+ */
+class BooleanSerializer extends TypeSerializer<Boolean> {
+
+    BooleanSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(Boolean value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/ByteSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/ByteSerializer.java
new file mode 100644
index 0000000..ce26f99
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/ByteSerializer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link Byte} type.
+ */
+class ByteSerializer extends AbstractNumberSerializer<Byte> {
+
+    ByteSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(Byte value, JsonGenerator generator) {
+        generator.write(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/CalendarSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/CalendarSerializer.java
new file mode 100644
index 0000000..b6cc7b3
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/CalendarSerializer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * Serializer of the {@link Calendar} type.
+ */
+class CalendarSerializer extends AbstractDateSerializer<Calendar> {
+
+    CalendarSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(Calendar value) {
+        return value.toInstant();
+    }
+
+    @Override
+    protected String formatDefault(Calendar value, Locale locale) {
+        DateTimeFormatter formatter = value.isSet(Calendar.HOUR) || value.isSet(Calendar.HOUR_OF_DAY)
+                ? DateTimeFormatter.ISO_DATE_TIME
+                : DateTimeFormatter.ISO_DATE;
+        return formatter.withZone(value.getTimeZone().toZoneId())
+                .withLocale(locale).format(toTemporalAccessor(value));
+    }
+
+    @Override
+    protected TemporalAccessor toTemporalAccessor(Calendar object) {
+        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(object.getTimeInMillis()),
+                                       object.getTimeZone().toZoneId());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/CharSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/CharSerializer.java
new file mode 100644
index 0000000..0a5630e
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/CharSerializer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link Character} type.
+ */
+class CharSerializer extends TypeSerializer<Character> {
+
+    CharSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(Character value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(String.valueOf(value));
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/DateSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/DateSerializer.java
new file mode 100644
index 0000000..634da30
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/DateSerializer.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.util.Date;
+import java.util.Locale;
+
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+
+/**
+ * Serializer of the {@link Date} type.
+ */
+class DateSerializer<T extends Date> extends AbstractDateSerializer<T> {
+
+    private static final DateTimeFormatter DEFAULT_DATE_FORMATTER = DateTimeFormatter.ISO_DATE_TIME.withZone(UTC);
+
+    DateSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(Date value) {
+        return value.toInstant();
+    }
+
+    @Override
+    protected String formatDefault(Date value, Locale locale) {
+        return DEFAULT_DATE_FORMATTER.withLocale(locale).format(toInstant(value));
+    }
+
+    @Override
+    protected String formatWithFormatter(Date value, DateTimeFormatter formatter) {
+        return getZonedFormatter(formatter).format(toTemporalAccessor(value));
+    }
+
+    @Override
+    protected String formatStrictIJson(Date value) {
+        return JsonbDateFormatter.IJSON_DATE_FORMATTER.withZone(UTC).format(toTemporalAccessor(value));
+    }
+
+    @Override
+    protected TemporalAccessor toTemporalAccessor(Date object) {
+        return toInstant(object);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/DoubleSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/DoubleSerializer.java
new file mode 100644
index 0000000..99ab3fa
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/DoubleSerializer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link Double} type.
+ */
+class DoubleSerializer extends AbstractNumberSerializer<Double> {
+
+    DoubleSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(Double value, JsonGenerator generator) {
+        generator.write(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/DurationSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/DurationSerializer.java
new file mode 100644
index 0000000..46118d7
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/DurationSerializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Duration;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link Duration} type.
+ */
+class DurationSerializer extends TypeSerializer<Duration> {
+
+    DurationSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(Duration value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.toString());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/EnumSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/EnumSerializer.java
new file mode 100644
index 0000000..39b14ae
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/EnumSerializer.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link Enum} types.
+ */
+class EnumSerializer extends TypeSerializer<Enum<?>> {
+
+    EnumSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(Enum<?> value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.name());
+    }
+
+    @Override
+    void serializeKey(Enum<?> key, JsonGenerator generator, SerializationContextImpl context) {
+        generator.writeKey(key.name());
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/FloatSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/FloatSerializer.java
new file mode 100644
index 0000000..2a87f69
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/FloatSerializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.math.BigDecimal;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link Float} type.
+ */
+class FloatSerializer extends AbstractNumberSerializer<Float> {
+
+    FloatSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(Float value, JsonGenerator generator) {
+        //floats lose precision, after upcasting to doubles in jsonp
+        generator.write(new BigDecimal(String.valueOf(value)));
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/InstantSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/InstantSerializer.java
new file mode 100644
index 0000000..0ea3a57
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/InstantSerializer.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+
+/**
+ * Serializer of the {@link Instant} type.
+ */
+class InstantSerializer extends AbstractDateSerializer<Instant> {
+
+    InstantSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(Instant value) {
+        return value;
+    }
+
+    @Override
+    protected String formatDefault(Instant value, Locale locale) {
+        return DateTimeFormatter.ISO_INSTANT.withLocale(locale).format(value);
+    }
+
+    @Override
+    protected String formatWithFormatter(Instant value, DateTimeFormatter formatter) {
+        return formatter.withZone(UTC).format(value);
+    }
+
+    @Override
+    protected String formatStrictIJson(Instant value) {
+        return JsonbDateFormatter.IJSON_DATE_FORMATTER.withZone(UTC).format(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/IntegerSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/IntegerSerializer.java
new file mode 100644
index 0000000..fe0bf82
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/IntegerSerializer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link Integer} type.
+ */
+class IntegerSerializer extends AbstractNumberSerializer<Integer> {
+
+    IntegerSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(Integer value, JsonGenerator generator) {
+        generator.write(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/JsonValueSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/JsonValueSerializer.java
new file mode 100644
index 0000000..9ef0c1e
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/JsonValueSerializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link JsonValue} type.
+ */
+class JsonValueSerializer extends TypeSerializer<JsonValue> {
+
+    JsonValueSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(JsonValue value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalDateSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalDateSerializer.java
new file mode 100644
index 0000000..f156c53
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalDateSerializer.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+
+/**
+ * Serializer of the {@link LocalDate} type.
+ */
+class LocalDateSerializer extends AbstractDateSerializer<LocalDate> {
+
+    private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE.withZone(UTC);
+
+    LocalDateSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(LocalDate value) {
+        return Instant.from(value.atStartOfDay(UTC));
+    }
+
+    @Override
+    protected String formatDefault(LocalDate value, Locale locale) {
+        return DEFAULT_FORMAT.withLocale(locale).format(value);
+    }
+
+    @Override
+    protected String formatStrictIJson(LocalDate value) {
+        final ZonedDateTime zonedDateTime = value.atTime(0, 0, 0).atZone(UTC);
+        return JsonbDateFormatter.IJSON_DATE_FORMATTER.withZone(UTC).format(zonedDateTime);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalDateTimeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalDateTimeSerializer.java
new file mode 100644
index 0000000..04ffa1d
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalDateTimeSerializer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import org.eclipse.yasson.internal.JsonbDateFormatter;
+
+/**
+ * Serializer of the {@link LocalDateTime} type.
+ */
+class LocalDateTimeSerializer extends AbstractDateSerializer<LocalDateTime> {
+
+    LocalDateTimeSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    protected Instant toInstant(LocalDateTime value) {
+        return value.atZone(UTC).toInstant();
+    }
+
+    @Override
+    protected String formatDefault(LocalDateTime value, Locale locale) {
+        return DateTimeFormatter.ISO_LOCAL_DATE_TIME.withLocale(locale).format(value);
+    }
+
+    @Override
+    protected String formatWithFormatter(LocalDateTime value, DateTimeFormatter formatter) {
+        return getZonedFormatter(formatter).format(value);
+    }
+
+    @Override
+    protected String formatStrictIJson(LocalDateTime value) {
+        final ZonedDateTime zonedDateTime = value.atZone(UTC);
+        return JsonbDateFormatter.IJSON_DATE_FORMATTER.format(zonedDateTime);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalTimeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalTimeSerializer.java
new file mode 100644
index 0000000..4be94fe
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/LocalTimeSerializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Serializer of the {@link LocalTime} type.
+ */
+class LocalTimeSerializer extends AbstractDateSerializer<LocalTime> {
+
+    LocalTimeSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(LocalTime value) {
+        throw new JsonbException(Messages.getMessage(MessageKeys.TIME_TO_EPOCH_MILLIS_ERROR, LocalTime.class.getSimpleName()));
+    }
+
+    @Override
+    protected String formatDefault(LocalTime value, Locale locale) {
+        return DateTimeFormatter.ISO_LOCAL_TIME.withLocale(locale).format(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/LongSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/LongSerializer.java
new file mode 100644
index 0000000..e522e7a
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/LongSerializer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link Long} type.
+ */
+class LongSerializer extends AbstractNumberSerializer<Long> {
+
+    LongSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(Long value, JsonGenerator generator) {
+        generator.write(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/MonthDayTypeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/MonthDayTypeSerializer.java
new file mode 100644
index 0000000..0108e46
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/MonthDayTypeSerializer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.MonthDay;
+import java.time.Year;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Serializer of the {@link MonthDay} type.
+ */
+class MonthDayTypeSerializer extends AbstractDateSerializer<MonthDay> {
+
+    private static final int YEAR_NUMBER = Year.now().getValue();
+
+    private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("--MM-dd").withZone(UTC);
+
+    MonthDayTypeSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(MonthDay value) {
+        return value.atYear(YEAR_NUMBER).atStartOfDay(UTC).toInstant();
+    }
+
+    @Override
+    protected String formatDefault(MonthDay value, Locale locale) {
+        return DEFAULT_FORMAT.withLocale(locale).format(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/NumberSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/NumberSerializer.java
new file mode 100644
index 0000000..0dff22b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/NumberSerializer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.math.BigDecimal;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link Number} type.
+ */
+class NumberSerializer extends AbstractNumberSerializer<Number> {
+
+    NumberSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(Number value, JsonGenerator generator) {
+        generator.write(new BigDecimal(String.valueOf(value)));
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/ObjectTypeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/ObjectTypeSerializer.java
new file mode 100644
index 0000000..db9ef95
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/ObjectTypeSerializer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.lang.reflect.Type;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.model.customization.Customization;
+import org.eclipse.yasson.internal.serializer.ModelSerializer;
+import org.eclipse.yasson.internal.serializer.SerializationModelCreator;
+
+/**
+ * Object type serializer. Dynamically resolves the serialized type based on the serialized instance class.
+ */
+public class ObjectTypeSerializer extends TypeSerializer<Object> {
+
+    private final Customization customization;
+
+    private final Map<Class<?>, ModelSerializer> cache;
+    private final List<Type> chain;
+    private final boolean isKey;
+
+    ObjectTypeSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+        this.customization = serializerBuilder.getCustomization();
+        this.cache = new ConcurrentHashMap<>();
+        this.chain = new LinkedList<>(serializerBuilder.getChain());
+        this.isKey = serializerBuilder.isKey();
+    }
+
+    @Override
+    void serializeValue(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        //Dynamically resolved type during runtime. Cached in SerializationModelCreator.
+        findSerializer(value, generator, context);
+    }
+
+    @Override
+    void serializeKey(Object key, JsonGenerator generator, SerializationContextImpl context) {
+        if (key == null) {
+            super.serializeKey(null, generator, context);
+            return;
+        }
+        //Dynamically resolved type during runtime. Cached in SerializationModelCreator.
+        findSerializer(key, generator, context);
+    }
+
+    private void findSerializer(Object key, JsonGenerator generator, SerializationContextImpl context) {
+        Class<?> clazz = key.getClass();
+        cache.computeIfAbsent(clazz, aClass -> {
+            SerializationModelCreator serializationModelCreator = context.getJsonbContext().getSerializationModelCreator();
+            return serializationModelCreator.serializerChainRuntime(new LinkedList<>(chain), clazz, customization, false, isKey);
+        }).serialize(key, generator, context);
+    }
+
+    /**
+     * Add serializer to the cache.
+     *
+     * @param clazz           class of the serializer
+     * @param modelSerializer model serializer bound to the class
+     */
+    public void addSpecificSerializer(Class<?> clazz, ModelSerializer modelSerializer) {
+        cache.put(clazz, modelSerializer);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/OffsetDateTimeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/OffsetDateTimeSerializer.java
new file mode 100644
index 0000000..eb463ea
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/OffsetDateTimeSerializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Serializer of the {@link OffsetDateTime} type.
+ */
+class OffsetDateTimeSerializer extends AbstractDateSerializer<OffsetDateTime> {
+
+    OffsetDateTimeSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(OffsetDateTime value) {
+        return value.toInstant();
+    }
+
+    @Override
+    protected String formatDefault(OffsetDateTime value, Locale locale) {
+        return DateTimeFormatter.ISO_OFFSET_DATE_TIME.withLocale(locale).format(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/OffsetTimeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/OffsetTimeSerializer.java
new file mode 100644
index 0000000..8610c19
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/OffsetTimeSerializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.OffsetTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Serializer of the {@link OffsetTime} type.
+ */
+class OffsetTimeSerializer extends AbstractDateSerializer<OffsetTime> {
+
+    OffsetTimeSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(OffsetTime value) {
+        throw new JsonbException(Messages.getMessage(MessageKeys.TIME_TO_EPOCH_MILLIS_ERROR, OffsetTime.class.getSimpleName()));
+    }
+
+    @Override
+    protected String formatDefault(OffsetTime value, Locale locale) {
+        return DateTimeFormatter.ISO_OFFSET_TIME.withLocale(locale).format(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalDoubleSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalDoubleSerializer.java
new file mode 100644
index 0000000..87b4e90
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalDoubleSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.util.OptionalDouble;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.serializer.ModelSerializer;
+
+/**
+ * Serializer of the {@link OptionalDouble} type.
+ */
+class OptionalDoubleSerializer implements ModelSerializer {
+
+    private final ModelSerializer typeSerializer;
+
+    OptionalDoubleSerializer(ModelSerializer typeSerializer) {
+        this.typeSerializer = typeSerializer;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        OptionalDouble optionalDouble = (OptionalDouble) value;
+        if (optionalDouble != null && optionalDouble.isPresent()) {
+            typeSerializer.serialize(optionalDouble.getAsDouble(), generator, context);
+        } else {
+            typeSerializer.serialize(null, generator, context);
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalIntSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalIntSerializer.java
new file mode 100644
index 0000000..e339830
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalIntSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.util.OptionalInt;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.serializer.ModelSerializer;
+
+/**
+ * Serializer of the {@link OptionalInt} type.
+ */
+class OptionalIntSerializer implements ModelSerializer {
+
+    private final ModelSerializer typeSerializer;
+
+    OptionalIntSerializer(ModelSerializer typeSerializer) {
+        this.typeSerializer = typeSerializer;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        OptionalInt optionalInt = (OptionalInt) value;
+        if (optionalInt != null && optionalInt.isPresent()) {
+            typeSerializer.serialize(optionalInt.getAsInt(), generator, context);
+        } else {
+            typeSerializer.serialize(null, generator, context);
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalLongSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalLongSerializer.java
new file mode 100644
index 0000000..772f245
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/OptionalLongSerializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.util.OptionalLong;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.serializer.ModelSerializer;
+
+/**
+ * Serializer of the {@link OptionalLong} type.
+ */
+class OptionalLongSerializer implements ModelSerializer {
+
+    private final ModelSerializer typeSerializer;
+
+    OptionalLongSerializer(ModelSerializer typeSerializer) {
+        this.typeSerializer = typeSerializer;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        OptionalLong optionalLong = (OptionalLong) value;
+        if (optionalLong != null && optionalLong.isPresent()) {
+            typeSerializer.serialize(optionalLong.getAsLong(), generator, context);
+        } else {
+            typeSerializer.serialize(null, generator, context);
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/PathSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/PathSerializer.java
new file mode 100644
index 0000000..57388cc
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/PathSerializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.nio.file.Path;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link Path} type.
+ */
+class PathSerializer extends TypeSerializer<Path> {
+
+    PathSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(Path value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.toString());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/PeriodSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/PeriodSerializer.java
new file mode 100644
index 0000000..d2f3e63
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/PeriodSerializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Period;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link Period} type.
+ */
+class PeriodSerializer extends TypeSerializer<Period> {
+
+    PeriodSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(Period value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.toString());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/ShortSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/ShortSerializer.java
new file mode 100644
index 0000000..17bc7eb
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/ShortSerializer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Serializer of the {@link Short} type.
+ */
+class ShortSerializer extends AbstractNumberSerializer<Short> {
+
+    ShortSerializer(TypeSerializerBuilder builder) {
+        super(builder);
+    }
+
+    @Override
+    void writeValue(Short value, JsonGenerator generator) {
+        generator.write(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/SqlDateSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/SqlDateSerializer.java
new file mode 100644
index 0000000..aad680f
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/SqlDateSerializer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Common serializer for {@link Date} and {@link java.sql.Date} types.
+ */
+class SqlDateSerializer extends DateSerializer<Date> {
+
+    SqlDateSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(Date value) {
+        if (value instanceof java.sql.Date) {
+            // java.sql.Date doesn't have a time component, so do our best if TIME_IN_MILLIS is requested
+            // In the future (at a breaking change boundary) we should probably reject this code path
+            return Instant.ofEpochMilli(value.getTime());
+        } else {
+            return super.toInstant(value);
+        }
+    }
+
+    @Override
+    protected String formatDefault(Date value, Locale locale) {
+        if (value instanceof java.sql.Date) {
+            return value.toString() + 'Z'; // Z is the UTC timezone indicator
+        } else {
+            return super.formatDefault(value, locale);
+        }
+    }
+
+    @Override
+    protected String formatWithFormatter(Date value, DateTimeFormatter formatter) {
+        if (value instanceof java.sql.Date) {
+            return ((java.sql.Date) value).toLocalDate().format(formatter);
+        } else {
+            return super.formatWithFormatter(value, formatter);
+        }
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/SqlTimestampSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/SqlTimestampSerializer.java
new file mode 100644
index 0000000..1660473
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/SqlTimestampSerializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.sql.Timestamp;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Serializer of the {@link Timestamp} type.
+ */
+class SqlTimestampSerializer extends AbstractDateSerializer<Timestamp> {
+
+    /**
+     * Default Yasson {@link DateTimeFormatter}.
+     */
+    private static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormatter.ISO_DATE_TIME.withZone(UTC);
+
+    SqlTimestampSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(Timestamp value) {
+        return value.toInstant();
+    }
+
+    @Override
+    protected String formatDefault(Timestamp value, Locale locale) {
+        return DEFAULT_FORMATTER.withLocale(locale).format(toInstant(value));
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/StringSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/StringSerializer.java
new file mode 100644
index 0000000..63b5309
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/StringSerializer.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.nio.charset.StandardCharsets;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.JsonbConfigProperties;
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Serializer of the {@link String} type.
+ */
+class StringSerializer extends TypeSerializer<String> {
+
+    StringSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(String value, JsonGenerator generator, SerializationContextImpl context) {
+        JsonbConfigProperties configProperties = context.getJsonbContext().getConfigProperties();
+        if (configProperties.isStrictIJson()) {
+            String newString = new String(value.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
+            if (!newString.equals(value)) {
+                throw new JsonbException(Messages.getMessage(MessageKeys.UNPAIRED_SURROGATE));
+            }
+        }
+        generator.write(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/TimeZoneSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/TimeZoneSerializer.java
new file mode 100644
index 0000000..e35e5f4
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/TimeZoneSerializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.util.TimeZone;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link TimeZone} type.
+ */
+class TimeZoneSerializer extends TypeSerializer<TimeZone> {
+
+    TimeZoneSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(TimeZone value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.getID());
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializer.java
new file mode 100644
index 0000000..badbdb9
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializer.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+import org.eclipse.yasson.internal.serializer.ModelSerializer;
+
+/**
+ * Base for all the type serializers.
+ */
+abstract class TypeSerializer<T> implements ModelSerializer {
+
+    private final ModelSerializer serializer;
+
+    TypeSerializer(TypeSerializerBuilder serializerBuilder) {
+        if (serializerBuilder.isKey()) {
+            serializer = new KeySerializer();
+        } else {
+            serializer = new ValueSerializer();
+        }
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+        serializer.serialize(value, generator, context);
+    }
+
+    abstract void serializeValue(T value, JsonGenerator generator, SerializationContextImpl context);
+
+    void serializeKey(T key, JsonGenerator generator, SerializationContextImpl context) {
+        generator.writeKey(String.valueOf(key));
+    }
+
+    private final class ValueSerializer implements ModelSerializer {
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            serializeValue((T) value, generator, context);
+        }
+
+    }
+
+    private final class KeySerializer implements ModelSerializer {
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public void serialize(Object value, JsonGenerator generator, SerializationContextImpl context) {
+            serializeKey((T) value, generator, context);
+        }
+
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializerBuilder.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializerBuilder.java
new file mode 100644
index 0000000..8425715
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializerBuilder.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.lang.reflect.Type;
+import java.util.List;
+
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.model.customization.Customization;
+
+/**
+ * Type serializer data holder object used during serializer creation.
+ */
+class TypeSerializerBuilder {
+
+    private final List<Type> chain;
+    private final Class<?> clazz;
+    private final Customization customization;
+    private final JsonbContext jsonbContext;
+    private final boolean key;
+
+    TypeSerializerBuilder(List<Type> chain,
+                          Class<?> clazz,
+                          Customization customization,
+                          JsonbContext jsonbContext,
+                          boolean key) {
+        this.chain = chain;
+        this.clazz = clazz;
+        this.customization = customization;
+        this.jsonbContext = jsonbContext;
+        this.key = key;
+    }
+
+    public List<Type> getChain() {
+        return chain;
+    }
+
+    public Class<?> getClazz() {
+        return clazz;
+    }
+
+    public Customization getCustomization() {
+        return customization;
+    }
+
+    public JsonbContext getJsonbContext() {
+        return jsonbContext;
+    }
+
+    public boolean isKey() {
+        return key;
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializers.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializers.java
new file mode 100644
index 0000000..c25fc89
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/TypeSerializers.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Period;
+import java.time.YearMonth;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.function.Function;
+
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import jakarta.json.JsonNumber;
+import jakarta.json.JsonString;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.internal.JsonbContext;
+import org.eclipse.yasson.internal.model.customization.Customization;
+import org.eclipse.yasson.internal.serializer.ModelSerializer;
+import org.eclipse.yasson.internal.serializer.SerializationModelCreator;
+
+import static org.eclipse.yasson.internal.BuiltInTypes.isClassAvailable;
+
+/**
+ * Specific type serializers.
+ */
+public class TypeSerializers {
+
+    private static final Map<Class<?>, Function<TypeSerializerBuilder, ModelSerializer>> SERIALIZERS;
+    private static final Set<Class<?>> SUPPORTED_MAP_KEYS;
+
+    private static final Map<Class<?>, Class<?>> OPTIONALS;
+
+    static {
+        Map<Class<?>, Function<TypeSerializerBuilder, ModelSerializer>> cache = new HashMap<>();
+        cache.put(Byte.class, ByteSerializer::new);
+        cache.put(Byte.TYPE, ByteSerializer::new);
+        cache.put(BigDecimal.class, BigDecimalSerializer::new);
+        cache.put(BigInteger.class, BigIntegerSerializer::new);
+        cache.put(Boolean.class, BooleanSerializer::new);
+        cache.put(Boolean.TYPE, BooleanSerializer::new);
+        cache.put(Calendar.class, CalendarSerializer::new);
+        cache.put(Character.class, CharSerializer::new);
+        cache.put(Character.TYPE, CharSerializer::new);
+        cache.put(Date.class, DateSerializer::new);
+        cache.put(Double.class, DoubleSerializer::new);
+        cache.put(Double.TYPE, DoubleSerializer::new);
+        cache.put(Duration.class, DurationSerializer::new);
+        cache.put(Float.class, FloatSerializer::new);
+        cache.put(Float.TYPE, FloatSerializer::new);
+        cache.put(Integer.class, IntegerSerializer::new);
+        cache.put(Integer.TYPE, IntegerSerializer::new);
+        cache.put(Instant.class, InstantSerializer::new);
+        cache.put(LocalDateTime.class, LocalDateTimeSerializer::new);
+        cache.put(LocalDate.class, LocalDateSerializer::new);
+        cache.put(LocalTime.class, LocalTimeSerializer::new);
+        cache.put(Long.class, LongSerializer::new);
+        cache.put(Long.TYPE, LongSerializer::new);
+        cache.put(MonthDay.class, MonthDayTypeSerializer::new);
+        cache.put(Number.class, NumberSerializer::new);
+        cache.put(Object.class, ObjectTypeSerializer::new);
+        cache.put(OffsetDateTime.class, OffsetDateTimeSerializer::new);
+        cache.put(OffsetTime.class, OffsetTimeSerializer::new);
+        cache.put(Path.class, PathSerializer::new);
+        cache.put(Period.class, PeriodSerializer::new);
+        cache.put(Short.class, ShortSerializer::new);
+        cache.put(Short.TYPE, ShortSerializer::new);
+        cache.put(String.class, StringSerializer::new);
+        cache.put(TimeZone.class, TimeZoneSerializer::new);
+        cache.put(URI.class, UriSerializer::new);
+        cache.put(URL.class, UrlSerializer::new);
+        cache.put(UUID.class, UuidSerializer::new);
+        if (isClassAvailable("javax.xml.datatype.XMLGregorianCalendar")) {
+            cache.put(XMLGregorianCalendar.class, XmlGregorianCalendarSerializer::new);
+        }
+        cache.put(YearMonth.class, YearMonthTypeSerializer::new);
+        cache.put(ZonedDateTime.class, ZonedDateTimeSerializer::new);
+        cache.put(ZoneId.class, ZoneIdSerializer::new);
+        cache.put(ZoneOffset.class, ZoneOffsetSerializer::new);
+        if (isClassAvailable("java.sql.Date")) {
+            cache.put(Date.class, SqlDateSerializer::new);
+            cache.put(java.sql.Date.class, SqlDateSerializer::new);
+            cache.put(java.sql.Timestamp.class, SqlTimestampSerializer::new);
+        }
+        SERIALIZERS = Map.copyOf(cache);
+
+        Map<Class<?>, Class<?>> optionals = new HashMap<>();
+        optionals.put(OptionalDouble.class, Double.class);
+        optionals.put(OptionalInt.class, Integer.class);
+        optionals.put(OptionalLong.class, Long.class);
+        OPTIONALS = Map.copyOf(optionals);
+
+        Set<Class<?>> mapKeys = new HashSet<>(SERIALIZERS.keySet());
+        mapKeys.addAll(optionals.keySet());
+        mapKeys.add(JsonNumber.class);
+        mapKeys.add(JsonString.class);
+        mapKeys.remove(Object.class);
+        SUPPORTED_MAP_KEYS = Set.copyOf(mapKeys);
+
+    }
+
+    private TypeSerializers() {
+        throw new IllegalStateException("Util class cannot be instantiated");
+    }
+
+    /**
+     * Whether type is the supported key type.
+     *
+     * @param clazz key type
+     * @return whether type is supported key type
+     */
+    public static boolean isSupportedMapKey(Class<?> clazz) {
+        return Enum.class.isAssignableFrom(clazz) || SUPPORTED_MAP_KEYS.contains(clazz);
+    }
+
+    /**
+     * Create new type serializer.
+     *
+     * @param clazz         type of the serializer
+     * @param customization serializer customization
+     * @param jsonbContext  jsonb context
+     * @return new type serializer
+     */
+    public static ModelSerializer getTypeSerializer(Class<?> clazz, Customization customization, JsonbContext jsonbContext) {
+        return getTypeSerializer(Collections.emptyList(), clazz, customization, jsonbContext, false);
+    }
+
+    /**
+     * Create new type serializer.
+     *
+     * @param chain         chain of the type predecessors
+     * @param clazz         type of the serializer
+     * @param customization serializer customization
+     * @param jsonbContext  jsonb context
+     * @param key           whether serializer is a key
+     * @return new type serializer
+     */
+    public static ModelSerializer getTypeSerializer(List<Type> chain,
+                                                    Class<?> clazz,
+                                                    Customization customization,
+                                                    JsonbContext jsonbContext,
+                                                    boolean key) {
+        Class<?> current = clazz;
+        List<Type> chainClone = new LinkedList<>(chain);
+        TypeSerializerBuilder builder = new TypeSerializerBuilder(chainClone, clazz, customization, jsonbContext, key);
+        ModelSerializer typeSerializer = null;
+        if (Object.class.equals(current)) {
+            return SERIALIZERS.get(current).apply(builder);
+        }
+        if (OPTIONALS.containsKey(current)) {
+            Class<?> optionalInner = OPTIONALS.get(current);
+            ModelSerializer serializer = getTypeSerializer(chainClone, optionalInner, customization, jsonbContext, key);
+            if (OptionalInt.class.equals(current)) {
+                return new OptionalIntSerializer(serializer);
+            } else if (OptionalLong.class.equals(current)) {
+                return new OptionalLongSerializer(serializer);
+            } else if (OptionalDouble.class.equals(current)) {
+                return new OptionalDoubleSerializer(serializer);
+            } else {
+                throw new JsonbException("Unsupported Optional type for serialization: " + clazz);
+            }
+        }
+
+        if (Enum.class.isAssignableFrom(clazz)) {
+            typeSerializer = new EnumSerializer(builder);
+        } else if (JsonValue.class.isAssignableFrom(clazz)) {
+            typeSerializer = new JsonValueSerializer(builder);
+        }
+        if (typeSerializer == null) {
+            do {
+                if (SERIALIZERS.containsKey(current)) {
+                    typeSerializer = SERIALIZERS.get(current).apply(builder);
+                    break;
+                }
+                current = current.getSuperclass();
+            } while (!Object.class.equals(current) && current != null);
+        }
+
+        if (key) {
+            //We do not want any other special serializers around our type serializer if it will be used as a key
+            return typeSerializer;
+        }
+        return typeSerializer == null
+                ? null
+                : SerializationModelCreator.wrapInCommonSet(typeSerializer, customization, jsonbContext);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/UriSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/UriSerializer.java
new file mode 100644
index 0000000..01a9844
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/UriSerializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.net.URI;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link URI} type.
+ */
+class UriSerializer extends TypeSerializer<URI> {
+
+    UriSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(URI value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.toString());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/UrlSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/UrlSerializer.java
new file mode 100644
index 0000000..37f9ef1
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/UrlSerializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.net.URL;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link URL} type.
+ */
+class UrlSerializer extends TypeSerializer<URL> {
+
+    UrlSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(URL value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.toString());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/UuidSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/UuidSerializer.java
new file mode 100644
index 0000000..1a46f1b
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/UuidSerializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.util.UUID;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link UUID} type.
+ */
+class UuidSerializer extends TypeSerializer<UUID> {
+
+    UuidSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(UUID value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.toString());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/XmlGregorianCalendarSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/XmlGregorianCalendarSerializer.java
new file mode 100644
index 0000000..479bd48
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/XmlGregorianCalendarSerializer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+import java.util.Locale;
+
+import javax.xml.datatype.XMLGregorianCalendar;
+
+/**
+ * Serializer of the {@link XMLGregorianCalendar} type.
+ */
+class XmlGregorianCalendarSerializer extends AbstractDateSerializer<XMLGregorianCalendar> {
+
+    XmlGregorianCalendarSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(XMLGregorianCalendar value) {
+        return Instant.ofEpochMilli(value.toGregorianCalendar().getTimeInMillis());
+    }
+
+    @Override
+    protected String formatDefault(XMLGregorianCalendar value, Locale locale) {
+        DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
+        return formatter
+                .withLocale(locale)
+                .withZone(value.toGregorianCalendar().getTimeZone().toZoneId())
+                .format(toTemporalAccessor(value));
+    }
+
+    @Override
+    protected TemporalAccessor toTemporalAccessor(XMLGregorianCalendar object) {
+        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(object.toGregorianCalendar().getTimeInMillis()),
+                                       object.toGregorianCalendar().getTimeZone().toZoneId());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/YearMonthTypeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/YearMonthTypeSerializer.java
new file mode 100644
index 0000000..fd357ef
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/YearMonthTypeSerializer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Serializer of the {@link YearMonth} type.
+ */
+class YearMonthTypeSerializer extends AbstractDateSerializer<YearMonth> {
+
+    private static final DateTimeFormatter DEFAULT_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM").withZone(UTC);
+
+    YearMonthTypeSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(YearMonth value) {
+        return value.atDay(1).atStartOfDay(UTC).toInstant();
+    }
+
+    @Override
+    protected String formatDefault(YearMonth value, Locale locale) {
+        return DEFAULT_FORMAT.withLocale(locale).format(value);
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/ZoneIdSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/ZoneIdSerializer.java
new file mode 100644
index 0000000..4a34917
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/ZoneIdSerializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.ZoneId;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link ZoneId} type.
+ */
+class ZoneIdSerializer extends TypeSerializer<ZoneId> {
+
+    ZoneIdSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(ZoneId value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.getId());
+    }
+
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/ZoneOffsetSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/ZoneOffsetSerializer.java
new file mode 100644
index 0000000..420485d
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/ZoneOffsetSerializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.ZoneOffset;
+
+import jakarta.json.stream.JsonGenerator;
+
+import org.eclipse.yasson.internal.SerializationContextImpl;
+
+/**
+ * Serializer of the {@link ZoneOffset} type.
+ */
+class ZoneOffsetSerializer extends TypeSerializer<ZoneOffset> {
+
+    ZoneOffsetSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    void serializeValue(ZoneOffset value, JsonGenerator generator, SerializationContextImpl context) {
+        generator.write(value.getId());
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/types/ZonedDateTimeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/types/ZonedDateTimeSerializer.java
new file mode 100644
index 0000000..0891103
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/types/ZonedDateTimeSerializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer.types;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+/**
+ * Serializer of the {@link ZonedDateTime} type.
+ */
+class ZonedDateTimeSerializer extends AbstractDateSerializer<ZonedDateTime> {
+
+    ZonedDateTimeSerializer(TypeSerializerBuilder serializerBuilder) {
+        super(serializerBuilder);
+    }
+
+    @Override
+    protected Instant toInstant(ZonedDateTime value) {
+        return value.toInstant();
+    }
+
+    @Override
+    protected String formatDefault(ZonedDateTime value, Locale locale) {
+        return DateTimeFormatter.ISO_ZONED_DATE_TIME.withLocale(locale).format(value);
+    }
+}
diff --git a/src/main/java/org/eclipse/yasson/spi/JsonbComponentInstanceCreator.java b/src/main/java/org/eclipse/yasson/spi/JsonbComponentInstanceCreator.java
new file mode 100644
index 0000000..276ed17
--- /dev/null
+++ b/src/main/java/org/eclipse/yasson/spi/JsonbComponentInstanceCreator.java
@@ -0,0 +1,48 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.spi;
+
+import java.io.Closeable;
+
+/**
+ * Creates instances of JsonbComponents such as JsonbAdapter.
+ *
+ * <p>
+ * Yasson attempts to load the implementations using {@link java.util.ServiceLoader} first. If there are multiple
+ * implementations found the service provider with the highest priority is used. If there are no service providers found the
+ * default implementation is used.
+ * </p>
+ */
+public interface JsonbComponentInstanceCreator extends Closeable {
+
+    /**
+     * Default component priority.
+     */
+    int DEFAULT_PRIORITY = 0;
+
+    /**
+     * Returns instance of JsonbComponent for desired class.
+     *
+     * @param <T>            Jsonb component type
+     * @param componentClass component class
+     * @return component instance
+     */
+    <T> T getOrCreateComponent(Class<T> componentClass);
+
+    /**
+     * @return the priority of the component
+     */
+    default int getPriority() {
+        return DEFAULT_PRIORITY;
+    }
+}
diff --git a/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java b/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
new file mode 100644
index 0000000..2f3d2dc
--- /dev/null
+++ b/src/main/java16/org/eclipse/yasson/internal/ClassMultiReleaseExtension.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2021, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Optional;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+
+import org.eclipse.yasson.internal.model.JsonbCreator;
+import org.eclipse.yasson.internal.model.Property;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+/**
+ * Search for instance creator from other sources.
+ * Mainly intended to add extensibility for different java versions and new features.
+ */
+public class ClassMultiReleaseExtension {
+
+    private ClassMultiReleaseExtension() {
+        throw new IllegalStateException("This class cannot be instantiated");
+    }
+
+    static boolean shouldTransformToPropertyName(Method method) {
+        return !method.getDeclaringClass().isRecord();
+    }
+
+    static boolean isSpecialAccessorMethod(Method method, Map<String, Property> classProperties) {
+        return isRecord(method.getDeclaringClass())
+                && method.getParameterCount() == 0
+                && !void.class.equals(method.getReturnType())
+                && classProperties.containsKey(method.getName());
+    }
+
+    static JsonbCreator findCreator(Class<?> clazz,
+                                    Constructor<?>[] declaredConstructors,
+                                    AnnotationIntrospector introspector,
+                                    PropertyNamingStrategy propertyNamingStrategy) {
+        if (clazz.isRecord()) {
+            if (declaredConstructors.length == 1) {
+                return introspector.createJsonbCreator(declaredConstructors[0], null, clazz, propertyNamingStrategy);
+            }
+        }
+        return null;
+    }
+
+    public static boolean isRecord(Class<?> clazz) {
+        return clazz.isRecord();
+    }
+
+    public static Optional<JsonbException> exceptionToThrow(Class<?> clazz) {
+        if (clazz.isRecord()) {
+            if (clazz.getDeclaredConstructors().length > 1) {
+                return Optional.of(new JsonbException(Messages.getMessage(MessageKeys.RECORD_MULTIPLE_CONSTRUCTORS, clazz)));
+            }
+        }
+        return Optional.empty();
+    }
+
+}
diff --git a/src/main/resources/META-INF/native-image/org.eclipse/yasson/native-image.properties b/src/main/resources/META-INF/native-image/org.eclipse/yasson/native-image.properties
new file mode 100644
index 0000000..4762dac
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/org.eclipse/yasson/native-image.properties
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2019, 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,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+# This adds command line options to native image tool
+Args=-H:IncludeResourceBundles=yasson-messages
\ No newline at end of file
diff --git a/src/main/resources/META-INF/services/jakarta.json.bind.spi.JsonbProvider b/src/main/resources/META-INF/services/jakarta.json.bind.spi.JsonbProvider
new file mode 100644
index 0000000..311bdd6
--- /dev/null
+++ b/src/main/resources/META-INF/services/jakarta.json.bind.spi.JsonbProvider
@@ -0,0 +1 @@
+org.eclipse.yasson.JsonBindingProvider
\ No newline at end of file
diff --git a/src/main/resources/yasson-messages.properties b/src/main/resources/yasson-messages.properties
new file mode 100644
index 0000000..4d22449
--- /dev/null
+++ b/src/main/resources/yasson-messages.properties
@@ -0,0 +1,89 @@
+#
+# Copyright (c) 2016, 2022 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,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+# The following 2 comment lines are for automated tools that the IBM translation teams use
+#NLS_MESSAGEFORMAT_VAR
+#NLS_ENCODING=UNICODE
+
+cannotMarshallObject=Cannot marshall object
+typeResolutionError=Error resolving runtime type for type: {0}
+genericBoundNotFound=Generic bound not found for type {0} declared in {1}.
+cantConvertJsonValue=Cannot convert JSON value into: {0}
+cantCreateRootInstance=Cannot create root instance
+invokingGetter=Error invoking getter method.
+gettingValue=Error getting field value.
+settingValue=Error setting field value.
+noLoggerName=Logger name has to be set
+resolveParametrizedType=Cannot resolve ParameterizedType superclass for: {0}
+cantCreateInstance=Cannot create instance
+inferTypeForUnmarshall=Cannot infer a type for unmarshalling into: {0}
+implClassIncompatible=Implementation class {0} is not a subtype of interface {1}.
+notValueType=Not a value type: {0}
+unexpectedParseEvent=Unexpected parser event: {0}
+processFromJson=Process class: {0} from json using converter: {1}
+processToJson=Process class: {0} to json using converter: {1}
+creatingHandles=Error creating handles.
+settingValueWith=Error setting value with: {0}
+gettingValueWith=Error getting value with: {0}
+unpairedSurrogate=String contains unpaired surrogate.
+adapterException=Problem adapting object of type {0} to {1} in class {2}
+adapterFound=Found adapter from type {0} to type {1}.
+adapterIncompatible=Adapter of runtime type {0} does not match property type {1}
+propertyOrder=Property order strategy with name {0} was not recognized
+unknownVisibilityStrategy=Property visibility strategy with name {0} was not recognized
+unsupportedJsonpSerializerValue=Unsupported value of type {0} for JSON serializer.
+noJndiEnvironment=No JNDI environment ({0}) found to look for CDI provider.
+noCdiApiProvider=CDI API not found on class or module path {0}.
+illegalAccess=Yasson does not have sufficient permissions to {0}.
+usingDefaultConstructorInstantiator=CDI bean manager not found, serializers and adapters will not have CDI support.
+noCdiEnvironment=CDI environment is not available.
+iJsonEnabledSingleValue=Cannot marshal single value because I-Json is enabled!
+dateTypeNotSupported=Date type not supported: {0}
+errorParsingDate=Error parsing {1} from value: {0}. Check your @JsonbDateFormat has all time units for {1} type, \
+  or consider using org.eclipse.yasson.YassonConfig#ZERO_TIME_PARSE_DEFAULTING.
+noDefaultConstructor=Cannot create instance of a class: {0}, No default constructor found.
+recordMultipleConstructors=Cannot create instance of a record: {0}, Multiple constructors found.
+offsetDateTimeFromMillis=Parsing {0} from epoch millisecond, UTC zone offset will be used.
+timeToEpochMillisError=Cannot convert {0} to/from epoch milliseconds.
+configPropertyInvalidType=JsonbConfig property [{0}] must be of type [{1}].
+conversionNotSupported=Conversion of type [{0}] to/from string value is not supported.
+endOfJsonStructure=End of JSON structure reached.
+invalidDeserializationType=Invalid json value type {0} for deserialization into type {1}.
+czechDiacritics=p\u0159\u00edli\u0161,\u017elu\u0165ou\u010dk\u00fd,k\u016f\u0148,\u00fap\u011bl,\u010f\u00e1belsk\u00e9,\u00f3dy
+russianDiacritics=\u0441\u043b\u0438\u0448\u043a\u043e\u043c,\u0436\u0435\u043b\u0442\u0435\u043d\u044c\u043a\u0438\u0439,\u043a\u043e\u043d\u044c,\u043f\u0435\u043b,\u0430\u0434\u0441\u043a\u0438\u0435,\u043e\u0434\u044b
+propertyNotFoundDeserializer=Property {0} was not found in class {1}. Skipping value {2}.
+settingPropertyDeserializer=Setting property {0} in class {1} with value {2}.
+classLoadNotAllowed=Class load for {0} name is not allowed!
+errorCallingJsonbCreator=Exception occurred during call to JSONB creator on class: {0}.
+incompatibleFactoryCreatorReturnType=Return type of creator {0} must be of type {1}.
+multipleJsonbCreators=More than one @JsonbCreator declared in class {0}.
+internalError=Internal error: {0}
+serializePropertyError=Unable to serialize property ''{0}'' from {1}
+deserializeValueError=Error deserialize JSON value into type: {0}.
+parsingNumber=Error parsing number {0} with format {1}.
+unknownBinaryDataStrategy=Unknown binary data strategy: {0}
+invalidPropertyNamingStrategy=Property naming strategy must be either: String name, or instance of jakarta.json.bind.config.PropertyNamingStrategy.
+creatorParameterNotAnnotated=Argument has no JsonbProperty annotation for JsonbCreator {0}.
+unknownJsonProperty=Json property {0} can not be mapped to a class {1}.
+jsonbCreatorMissingProperty=JsonbCreator parameter {0} is missing in json document.
+zoneParseError=Cannot parse zone from json value: {0}
+jsonbTransientWithOtherAnnotations=JsonbTransient annotation cannot be used with other jsonb annotations on the same property.
+nonParametrizedType=Type: {0} is not a parametrized type.
+propertyNameClash=Property {0} clashes with property {1} by read or write name in class {2}.
+sqlDateIJsonError=java.sql.Date is not supported in STRICT_IJSON mode.
+recursiveReference=Recursive reference has been found in class {0}.
+datatypeFactoryCreationFailed=An error occurred while DatatypeFactory creation.
+multipleConstructorPropertiesCreators=More than one constructor annotated with @ConstructorProperties declared in class {0}.
+annotationNotAvailable=Annotation {0} is not visible in modules or classpath. Annotation will be ignored.
+missingValuePropertyInAnnotation=Missing value property in Annotation {0}. Annotation will be ignored.
+numberIncompatibleValueTypeArray=Value type {0} is not a JsonNumber.
+numberIncompatibleValueTypeObject=Value type {0} at key {1} is not a JsonNumber.
diff --git a/src/test/java/PackagelessClassTest.java b/src/test/java/PackagelessClassTest.java
new file mode 100644
index 0000000..edf7198
--- /dev/null
+++ b/src/test/java/PackagelessClassTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+/**
+ * Tests the serialization/deserialization of a class that has no package.
+ *
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class PackagelessClassTest {
+
+    @Test
+    public void testSerialization() throws Exception {
+        PackagelessModel packagelessClass = new PackagelessModel(12, "Hello World!");
+
+        String expected = "{\"intValue\":12,\"stringValue\":\"Hello World!\"}";
+        assertEquals(expected, defaultJsonb.toJson(packagelessClass));
+    }
+
+    @Test
+    public void testDeSerialization() throws Exception {
+        PackagelessModel packagelessClass = new PackagelessModel(12, "Hello World!");
+
+        String input = "{\"intValue\":12,\"stringValue\":\"Hello World!\"}";
+        PackagelessModel packagelessModel = defaultJsonb.fromJson(input, PackagelessModel.class);
+        assertEquals(packagelessModel.getIntValue(), 12);
+        assertEquals(packagelessModel.getStringValue(), "Hello World!");
+    }
+}
diff --git a/src/test/java/PackagelessModel.java b/src/test/java/PackagelessModel.java
new file mode 100644
index 0000000..4d9357e
--- /dev/null
+++ b/src/test/java/PackagelessModel.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/**
+ * This class is designed outside any packages intentionally to test serialization/deserialization scenarios for such
+ * cases.
+ *
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class PackagelessModel {
+    private int intValue;
+    private String stringValue;
+
+    public PackagelessModel() {
+    }
+
+    public PackagelessModel(int intValue, String stringValue) {
+        this.intValue = intValue;
+        this.stringValue = stringValue;
+    }
+
+    public int getIntValue() {
+        return intValue;
+    }
+
+    public void setIntValue(int intValue) {
+        this.intValue = intValue;
+    }
+
+    public String getStringValue() {
+        return stringValue;
+    }
+
+    public void setStringValue(String stringValue) {
+        this.stringValue = stringValue;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/Assertions.java b/src/test/java/org/eclipse/yasson/Assertions.java
new file mode 100644
index 0000000..8b23d7c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/Assertions.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import jakarta.json.bind.JsonbException;
+
+public class Assertions {
+	
+	/**
+	 * Asserts that the given operation will fail with a JsonbException
+	 * @param operation The operation that is expected to fail
+	 */
+	public static void shouldFail(Supplier<?> operation) {
+		shouldFail(operation, JsonbException.class, msg -> true);
+	}
+	
+	public static void shouldFail(Runnable operation) {
+		shouldFail(() -> {
+			operation.run();
+			return null;
+		});
+	}
+	
+	/**
+	 * Asserts that the given operation will fail with a JsonbException
+	 * @param operation The operation that is expected to fail
+	 * @param checkExceptionMessage Any checks that should be made on the exception message. For example, ensuring the exception
+	 * includes a specific token. 
+	 */
+	public static void shouldFail(Supplier<?> operation, Function<String,Boolean> checkExceptionMessage) {
+		shouldFail(operation, JsonbException.class, checkExceptionMessage);
+	}
+
+	/**
+	 * Asserts that the given operation will fail
+	 * @param operation The operation that is expected to fail
+	 * @param expectedType The expected exception type to receive when evaluating the operation
+	 * @param checkExceptionMessage Any checks that should be made on the exception message. For example, ensuring the exception
+	 * includes a specific token. 
+	 */
+	public static void shouldFail(Supplier<?> operation, Class<? extends Throwable> expectedType, Function<String,Boolean> checkExceptionMessage) {
+		try {
+			operation.get();
+			fail("The operation should have failed with a " + expectedType.getCanonicalName() + " but it succeeded.");
+		} catch (Throwable t) {
+			String fullErrorMessage = "";
+			for (Throwable current = t; current != null && current.getCause() != current; current = current.getCause()) {
+			    fullErrorMessage += current.getClass().getCanonicalName() + ": ";
+				fullErrorMessage += current.getMessage() + "\n";
+			}
+			if (expectedType.isAssignableFrom(t.getClass())) {
+				if (!checkExceptionMessage.apply(fullErrorMessage)) {
+					t.printStackTrace();
+					fail("Exception did not contain the proper content: " + fullErrorMessage);
+				}
+			} else {
+				t.printStackTrace();
+				fail("Expected to get an exception of " + expectedType + " but instead was " + t.getClass());
+			}
+		}
+	}
+}
diff --git a/src/test/java/org/eclipse/yasson/DefaultGetterInInterface.java b/src/test/java/org/eclipse/yasson/DefaultGetterInInterface.java
new file mode 100644
index 0000000..9b5954e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/DefaultGetterInInterface.java
@@ -0,0 +1,149 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.internal.JsonbContext;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.spi.JsonProvider;
+import java.lang.reflect.Method;
+
+/**
+ *
+ * @author Maxence Laurent
+ */
+public class DefaultGetterInInterface {
+
+    public static interface Defaulted {
+
+        default public String getGetterA() {
+            return "valueA";
+        }
+    }
+
+    public static class PojoWithDefault implements Defaulted {
+    }
+
+    @Test
+    public void testWithDefault() {
+        PojoWithDefault pojo = new PojoWithDefault();
+        String result = defaultJsonb.toJson(pojo);
+        assertEquals("{\"getterA\":\"valueA\"}", result);
+    }
+
+    public static interface WithGetterI {
+
+        @JsonbProperty("withGetterI")
+        String getGetterI();
+    }
+
+    public static interface WithDefaultGetterI extends WithGetterI {
+
+        @Override
+        @JsonbProperty("default")
+        default String getGetterI() {
+            return "default";
+        }
+    }
+
+    public static interface OtherWithDefaultGetterI extends WithGetterI {
+
+        @Override
+        @JsonbProperty("otherDefault")
+        default String getGetterI() {
+            return "otherDefault";
+        }
+    }
+
+    public static class Pojo implements WithGetterI {
+
+        @Override
+        @JsonbProperty("implementation")
+        public String getGetterI() {
+            return "implementation";
+        }
+    }
+
+
+    public static class PojoNoAnnotation implements WithGetterI {
+
+        @Override
+        public String getGetterI() {
+            return "withGetterI";
+        }
+    }
+
+    public static class PojoWithDefaultSuperImplementation extends Pojo implements WithDefaultGetterI {
+    }
+
+    public static class PojoWithDefaultImplementation implements WithDefaultGetterI {
+
+        @Override
+        @JsonbProperty("defaultImplementation")
+        public String getGetterI() {
+            return "defaultImplementation";
+        }
+
+    }
+
+    public static class PojoWithDefaultOnly implements WithDefaultGetterI {
+    }
+
+    public static class PojoGetterDefaultedTwice extends PojoWithDefaultImplementation implements OtherWithDefaultGetterI {
+    }
+
+    @Test
+    public void testWithInheritedAndDefault() throws NoSuchMethodException {
+        JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider());
+
+        // direct implementation only (no default implementation)
+        Method pojoGetter = jsonbContext.getMappingContext().getOrCreateClassModel(Pojo.class).getPropertyModel("getterI").getGetter();
+
+        // default implementation
+        Method pojoDefaultOnlyGetter = jsonbContext.getMappingContext().getOrCreateClassModel(PojoWithDefaultOnly.class).getPropertyModel("getterI").getGetter();
+
+        // default implementation overriden by super class
+        Method pojoDefaultSuperGetter = jsonbContext.getMappingContext().getOrCreateClassModel(PojoWithDefaultSuperImplementation.class).getPropertyModel("getterI").getGetter();
+
+        // default implementation
+        Method pojoDefaultImplementationGetter = jsonbContext.getMappingContext().getOrCreateClassModel(PojoWithDefaultImplementation.class).getPropertyModel("getterI").getGetter();
+
+        // two default implementations
+        Method pojoTwoDefault = jsonbContext.getMappingContext().getOrCreateClassModel(PojoGetterDefaultedTwice.class).getPropertyModel("getterI").getGetter();
+
+        // assert getters selected by ClassParser
+        assertEquals(Pojo.class.getMethod("getGetterI"), pojoGetter);
+        assertEquals(WithDefaultGetterI.class.getMethod("getGetterI"), pojoDefaultOnlyGetter);
+        assertEquals(Pojo.class.getMethod("getGetterI"), pojoDefaultSuperGetter);
+        assertEquals(PojoWithDefaultImplementation.class.getMethod("getGetterI"), pojoDefaultImplementationGetter);
+        assertEquals(PojoWithDefaultImplementation.class.getMethod("getGetterI"), pojoTwoDefault);
+
+        // assert serialized json is correct, including property name as specified by JsonbProperty annotations
+        assertJson(new Pojo(), "implementation");
+        assertJson(new PojoNoAnnotation(), "withGetterI");
+        assertJson(new PojoWithDefaultOnly(), "default");
+        assertJson(new PojoWithDefaultSuperImplementation(), "implementation");
+        assertJson(new PojoWithDefaultImplementation(), "defaultImplementation");
+        assertJson(new PojoGetterDefaultedTwice(), "defaultImplementation");
+    }
+
+    private static void assertJson(WithGetterI pojo, String expected){
+        assertEquals(expected, pojo.getGetterI());
+        assertEquals("{\"" + expected + "\":\"" + pojo.getGetterI() + "\"}", defaultJsonb.toJson(pojo));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/FieldAccessStrategyTest.java b/src/test/java/org/eclipse/yasson/FieldAccessStrategyTest.java
new file mode 100644
index 0000000..1f04f14
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/FieldAccessStrategyTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */package org.eclipse.yasson;
+
+ import org.junit.jupiter.api.*;
+ import static org.junit.jupiter.api.Assertions.*;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbTransient;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class FieldAccessStrategyTest {
+
+    public static class PrivateFields {
+        private String strField;
+        @JsonbTransient
+        private boolean setterCalled;
+        @JsonbTransient
+        private boolean getterCalled;
+
+        public PrivateFields() {
+        }
+
+        public PrivateFields(String strField) {
+            this.strField = strField;
+        }
+
+        public String getStrField() {
+            getterCalled = true;
+            return strField;
+        }
+
+        public void setStrField(String strField) {
+            setterCalled = true;
+            this.strField = strField;
+        }
+    }
+
+    public static class PublicFields {
+        public String strField;
+    }
+
+
+    @Test
+    public void testPrivateFields() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new FieldAccessStrategy()));
+
+        PrivateFields pojo = new PrivateFields("pojo string");
+
+        String expected = "{\"strField\":\"pojo string\"}";
+
+        assertEquals(expected, jsonb.toJson(pojo));
+        PrivateFields result = jsonb.fromJson(expected, PrivateFields.class);
+        assertEquals(false, result.getterCalled);
+        assertEquals(false, result.setterCalled);
+        assertEquals("pojo string", result.strField);
+    }
+
+
+    @Test
+    public void testHidePublicFields() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new NoAccessStrategy()));
+
+        PublicFields pojo = new PublicFields();
+        pojo.strField = "string field";
+
+        String expected = "{}";
+
+        assertEquals(expected, jsonb.toJson(pojo));
+        PublicFields result = jsonb.fromJson("{\"strField\":\"pojo string\"}", PublicFields.class);
+        assertEquals(null, result.strField);
+    }
+
+    /**
+     * Ignores public / private, visibility is set by name.
+     */
+    @Test
+    public void testCustomVisibityStrategy() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyVisibilityStrategy(new CustomVisibilityStrategy()));
+
+        String json = "{\"floatInstance\":10.0,\"stringInstance\":\"Test String\"}";
+        SimpleContainer simpleContainer = new SimpleContainer();
+        simpleContainer.setStringInstance("Test String");
+        simpleContainer.setIntegerInstance(10);
+        simpleContainer.setFloatInstance(10.0f);
+        assertEquals(json, jsonb.toJson(simpleContainer));
+
+
+        SimpleContainer result = jsonb.fromJson("{ \"stringInstance\" : \"Test String\", \"floatInstance\" : 1.0, \"integerInstance\" : 1 }", SimpleContainer.class);
+        assertEquals("Test String", result.stringInstance);
+        assertNull(result.integerInstance);
+        assertNull(result.floatInstance);
+    }
+
+    public class CustomVisibilityStrategy implements PropertyVisibilityStrategy {
+        @Override
+        public boolean isVisible(Field field) {
+            return field.getName().equals("stringInstance");
+        }
+
+        @Override
+        public boolean isVisible(Method method) {
+            return method.getName().equals("getFloatInstance");
+        }
+    }
+    public static class SimpleContainer {
+        private String stringInstance;
+        private Integer integerInstance;
+        private Float floatInstance;
+
+        public String getStringInstance() {
+            return stringInstance;
+        }
+
+        public void setStringInstance(String stringInstance) {
+            this.stringInstance = stringInstance;
+        }
+
+        public Integer getIntegerInstance() {
+            return integerInstance;
+        }
+
+        public void setIntegerInstance(Integer integerInstance) {
+            this.integerInstance = integerInstance;
+        }
+
+        public float getFloatInstance() {
+            return floatInstance;
+        }
+
+        public void setFloatInstance(float floatInstance) {
+            this.floatInstance = floatInstance;
+        }
+    }
+
+
+    private static final class NoAccessStrategy implements PropertyVisibilityStrategy {
+
+        @Override
+        public boolean isVisible(Field field) {
+            return false;
+        }
+
+        @Override
+        public boolean isVisible(Method method) {
+            return false;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/Issue454Test.java b/src/test/java/org/eclipse/yasson/Issue454Test.java
new file mode 100644
index 0000000..fa2363a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/Issue454Test.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+public class Issue454Test {
+
+    @Test
+    public void test() {
+        final String EXPECTED = "{\"field2\":\"bbb\"}";
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+        assertEquals(EXPECTED, jsonb.toJson(new TheInterface() {
+
+            @Override
+            public String getField1() {
+                return "aaa";
+            }
+
+            @Override
+            public String getField2() {
+                return "bbb";
+            }}));
+        assertEquals(EXPECTED, jsonb.toJson(new TheClass() {
+            @Override
+            public String getField1() {
+                return "aaa";
+            }
+            @Override
+            public String getField2() {
+                return "bbb";
+            }}));
+        assertEquals(EXPECTED, jsonb.toJson(new TheClass2()));
+        assertEquals(EXPECTED, jsonb.toJson(new TheClass2() {}));
+    }
+    
+    public static abstract class TheClass {
+        @JsonbTransient
+        public abstract String getField1();
+
+        public abstract String getField2();
+    }
+
+    public static class TheClass2 extends TheClass {
+        @Override
+        public String getField1() {
+            return "aaa";
+        }
+        @Override
+        public String getField2() {
+            return "bbb";
+        }
+    }
+    
+    public static interface TheInterface {
+
+        @JsonbTransient
+        String getField1();
+
+        String getField2();
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/Issue456Test.java b/src/test/java/org/eclipse/yasson/Issue456Test.java
new file mode 100644
index 0000000..4bb4f89
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/Issue456Test.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.junit.jupiter.api.Test;
+
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbException;
+
+public class Issue456Test {
+
+    @Test
+    public void dontInvokeToString() {
+        try {
+            JsonbBuilder.create().toJson(new Example());
+            fail("JsonbException is expected");
+        } catch (JsonbException e) {
+            // Expected
+        }
+    }
+
+    public static class Example {
+
+        public String getProperty() {
+            throw new RuntimeException("some error");
+        }
+
+        @Override
+        public String toString() {
+            return JsonbBuilder.create().toJson(this);
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/JavaxNamingExcludedTest.java b/src/test/java/org/eclipse/yasson/JavaxNamingExcludedTest.java
new file mode 100644
index 0000000..dac6960
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/JavaxNamingExcludedTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.internal.cdi.NonCdiAdapter;
+import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory;
+
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+
+/**
+ * Requires --limit-modules java.base,java.logging,java.sql (to exclude java.naming) to work.
+ * See pom.xml surefire plugin configuration.
+ */
+public class JavaxNamingExcludedTest {
+
+    @Test
+    public void testNoJavaxNamingModule() {
+        try {
+            Class.forName(JsonbComponentInstanceCreatorFactory.INITIAL_CONTEXT_CLASS);
+            fail("Class [" + JsonbComponentInstanceCreatorFactory.INITIAL_CONTEXT_CLASS
+                    + "] should not be available for this test.");
+        } catch (ClassNotFoundException e) {
+            //OK, java.naming is not observable
+        }
+
+        final String result = defaultJsonb.toJson(new AdaptedPojo());
+        assertEquals("{\"adaptedValue1\":1111,\"adaptedValue2\":1001,\"adaptedValue3\":1010}", result);
+    }
+
+    public static final class AdaptedPojo {
+        @JsonbTypeAdapter(NonCdiAdapter.class)
+        public String adaptedValue1 = "1111";
+
+        @JsonbTypeAdapter(NonCdiAdapter.class)
+        public String adaptedValue2 = "1001";
+
+        @JsonbTypeAdapter(NonCdiAdapter.class)
+        public String adaptedValue3 = "1010";
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/Jsonbs.java b/src/test/java/org/eclipse/yasson/Jsonbs.java
new file mode 100644
index 0000000..2640e7d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/Jsonbs.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import jakarta.json.bind.*;
+import org.eclipse.yasson.internal.*;
+
+public class Jsonbs {
+	public static final Jsonb defaultJsonb = JsonbBuilder.create();
+	public static final Jsonb bindingJsonb = new JsonBindingBuilder().build();
+	public static final Jsonb formattingJsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(Boolean.TRUE));
+	public static final Jsonb nullableJsonb = JsonbBuilder.create(new JsonbConfig().withNullValues(Boolean.TRUE));
+    public static final YassonJsonb yassonJsonb = (YassonJsonb) JsonbBuilder.create();
+	public static final YassonJsonb bindingYassonJsonb = (YassonJsonb) new JsonBindingProvider().create().build();
+}
diff --git a/src/test/java/org/eclipse/yasson/SimpleTest.java b/src/test/java/org/eclipse/yasson/SimpleTest.java
new file mode 100644
index 0000000..ca11319
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/SimpleTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.bindingJsonb;
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class SimpleTest {
+
+    @Test
+    public void testSimpleSerialize() {
+        final StringWrapper wrapper = new StringWrapper();
+        wrapper.setValue("abc");
+        final String val = bindingJsonb.toJson(wrapper);
+        assertEquals("{\"value\":\"abc\"}", val);
+    }
+
+    @Test
+    public void testSimpleDeserializer() {
+        final StringWrapper stringWrapper = defaultJsonb.fromJson("{\"value\":\"abc\"}", StringWrapper.class);
+        assertEquals("abc", stringWrapper.value);
+    }
+
+    public static class StringWrapper {
+
+        public String value;
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/TestTypeToken.java b/src/test/java/org/eclipse/yasson/TestTypeToken.java
new file mode 100644
index 0000000..6c6c458
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/TestTypeToken.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public abstract class TestTypeToken<T> {
+    public Type getType() {
+        return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/YassonConfigTest.java b/src/test/java/org/eclipse/yasson/YassonConfigTest.java
new file mode 100644
index 0000000..fc5eb0f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/YassonConfigTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests that the names of configuration fields in {@link YassonConfig} do not change.
+ */
+public class YassonConfigTest {
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testFailOnUnknownPropertiesUnchanged() {
+        assertEquals("jsonb.fail-on-unknown-properties", YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES);
+        assertEquals("jsonb.fail-on-unknown-properties", YassonProperties.FAIL_ON_UNKNOWN_PROPERTIES);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testUserTypeMappingUnchanged() {
+        assertEquals("jsonb.user-type-mapping", YassonConfig.USER_TYPE_MAPPING);
+        assertEquals("jsonb.user-type-mapping", YassonProperties.USER_TYPE_MAPPING);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testZeroTimeDefaultingUnchanged() {
+        assertEquals("jsonb.zero-time-defaulting", YassonConfig.ZERO_TIME_PARSE_DEFAULTING);
+        assertEquals("jsonb.zero-time-defaulting", YassonProperties.ZERO_TIME_PARSE_DEFAULTING);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testNullRootSerializerUnchanged() {
+        assertEquals("yasson.null-root-serializer", YassonConfig.NULL_ROOT_SERIALIZER);
+        assertEquals("yasson.null-root-serializer", YassonProperties.NULL_ROOT_SERIALIZER);
+    }
+    
+    @Test
+    public void testEagerInitClassesUnchanged() {
+        assertEquals("yasson.eager-parse-classes", YassonConfig.EAGER_PARSE_CLASSES);
+    }
+    
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/AdaptersTest.java b/src/test/java/org/eclipse/yasson/adapters/AdaptersTest.java
new file mode 100644
index 0000000..d3db6c9
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/AdaptersTest.java
@@ -0,0 +1,586 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.adapters.model.*;
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonString;
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+import static java.util.Collections.unmodifiableMap;
+
+/**
+ * Tests adapters to behave correctly.
+ *
+ * @author Roman Grigoriadi
+ */
+public class AdaptersTest {
+
+    public static class NonGenericPojo {
+        public String strValues;
+        public Box box;
+    }
+
+    @Test
+    public void testBoxToCrateNoGenerics() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {
+                new JsonbAdapter<Box, Crate>() {
+                    @Override
+                    public Crate adaptToJson(Box box) {
+                        final Crate crate = new Crate();
+                        crate.setCrateStrField("crateAdapted" + box.getBoxStrField());
+                        crate.setCrateIntField(box.getBoxIntegerField() + 1);
+                        return crate;
+                    }
+
+                    @Override
+                    public Box adaptFromJson(Crate crate) {
+                        Box box = new Box();
+                        box.setBoxStrField("boxAdapted" + crate.getCrateStrField());
+                        box.setBoxIntegerField(crate.getCrateIntField() + 1);
+                        return box;
+                    }
+                }
+        };
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo pojo = new AdaptedPojo();
+        Box box = new Box();
+        box.setBoxStrField("BoxStr");
+        box.setBoxIntegerField(10);
+        pojo.box = box;
+        String json = jsonb.toJson(pojo);
+        assertEquals("{\"box\":{\"crateIntField\":11,\"crateStrField\":\"crateAdaptedBoxStr\"}}", json);
+
+        AdaptedPojo<?> result = jsonb.fromJson("{\"box\":{\"crateIntField\":10,\"crateStrField\":\"CrateStr\"}}", AdaptedPojo.class);
+        assertEquals(Integer.valueOf(11), result.box.getBoxIntegerField());
+        assertEquals("boxAdaptedCrateStr", result.box.getBoxStrField());
+    }
+
+    @Test
+    public void testValueFieldAdapter() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {
+                new JsonbAdapter<Integer, String>() {
+                    @Override
+                    public String adaptToJson(Integer integer) {
+                        return String.valueOf(integer);
+                    }
+
+                    @Override
+                    public Integer adaptFromJson(String s) {
+                        return Integer.valueOf(s);
+                    }
+                }
+        };
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo pojo = new AdaptedPojo();
+        pojo.intField = 11;
+        String json = jsonb.toJson(pojo);
+        assertEquals("{\"intField\":\"11\"}", json);
+
+        AdaptedPojo<?> result = jsonb.fromJson("{\"intField\":\"10\"}", AdaptedPojo.class);
+        assertEquals(Integer.valueOf(10), result.intField);
+    }
+
+    @Test
+    public void testGenericAdapter() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {new BoxToCrateCompatibleGenericsAdapter<Integer>() {
+        }};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo<Integer> pojo = new AdaptedPojo<>();
+        pojo.strField = "POJO_STRING";
+        pojo.intBox = new GenericBox<>("INT_BOX_STR", 11);
+        pojo.tBox = new GenericBox<>("T_BOX_STR", 110);
+
+        String marshalledJson = jsonb.toJson(pojo, new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals("{\"intBox\":{\"adaptedT\":11,\"crateStrField\":\"INT_BOX_STR\"}," +
+                "\"strField\":\"POJO_STRING\"," +
+                "\"tBox\":{\"adaptedT\":110,\"crateStrField\":\"T_BOX_STR\"}}", marshalledJson);
+
+        String toUnmarshall = "{\"intBox\":{\"crateStrField\":\"Box3\",\"adaptedT\":33}," +
+                "\"tBox\":{\"crateStrField\":\"tGenBoxCrateStr\",\"adaptedT\":22}," +
+                "\"strField\":\"POJO_STRING\"," +
+                "\"strBox\":{\"strField\":\"strBoxStr\",\"x\":\"44\"}}";
+        AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals("POJO_STRING", result.strField);
+        assertEquals("Box3", result.intBox.getStrField());
+        assertEquals(33, result.intBox.getX());
+        assertEquals("tGenBoxCrateStr", result.tBox.getStrField());
+        assertEquals(22, result.tBox.getX());
+        assertEquals("strBoxStr", result.strBox.getStrField());
+        assertEquals("44", result.strBox.getX());
+    }
+
+    @Test
+    public void testPropagatedTypeArgs() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {new BoxToCratePropagatedIntegerStringAdapter()};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo<Integer> pojo = new AdaptedPojo<>();
+        pojo.intBox = new GenericBox<>("INT_BOX_STR", 110);
+        pojo.tBox = new GenericBox<>("T_BOX_STR", 111);
+        pojo.strBox = new GenericBox<>("STR_BOX_STR", "101");
+
+        String marshalledJson = jsonb.toJson(pojo, new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals("{\"intBox\":{\"adaptedT\":{\"x\":[\"110\"]},\"crateStrField\":\"INT_BOX_STR\"}," +
+                        "\"strBox\":{\"strField\":\"STR_BOX_STR\",\"x\":\"101\"}," +
+                        "\"tBox\":{\"adaptedT\":{\"x\":[\"111\"]},\"crateStrField\":\"T_BOX_STR\"}}",
+                marshalledJson);
+
+        String toUnmarshall = "{\"intBox\":{\"crateStrField\":\"strCrateStr\",\"adaptedT\":{\"strField\":\"crateBoxStrField\",\"x\":[\"77\"]}}," +
+                "\"tBox\":{\"crateStrField\":\"tStrCrateStr\",\"adaptedT\":{\"strField\":\"crateBoxStrField\",\"x\":[\"88\"]}}," +
+                "\"strField\":\"POJO_STRING\"," +
+                "\"strBox\":{\"strField\":\"strBoxStr\",\"x\":\"44\"}}";
+
+        AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals("POJO_STRING", result.strField);
+        assertEquals("strCrateStr", result.intBox.getStrField());
+        assertEquals(77, result.intBox.getX());
+        assertEquals("tStrCrateStr", result.tBox.getStrField());
+        assertEquals(88, result.tBox.getX());
+        assertEquals("strBoxStr", result.strBox.getStrField());
+        assertEquals("44", result.strBox.getX());
+    }
+
+    @Test
+    public void testStringToGenericCollectionAdapter() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {new IntegerListToStringAdapter()};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo<List<Integer>> pojo = new AdaptedPojo<>();
+        pojo.tVar = Arrays.asList(11, 22, 33);
+        pojo.integerList = Arrays.asList(110, 111, 101);
+        String marshalledJson = jsonb.toJson(pojo, new TestTypeToken<AdaptedPojo<List<Integer>>>(){}.getType());
+        assertEquals("{\"integerList\":\"110#111#101\"," +
+                "\"tVar\":\"11#22#33\"}", marshalledJson);
+
+        String toUnmarshall = "{\"integerList\":\"11#22#33#44\",\"stringList\":[\"first\",\"second\"]," +
+                "\"tVar\":\"110#111#101\"}";
+
+        AdaptedPojo result = jsonb.fromJson(toUnmarshall, new TestTypeToken<AdaptedPojo<List<Integer>>>(){}.getType());
+        List<Integer> expectedIntegerList = Arrays.asList(11, 22, 33, 44);
+        List<String> expectedStringList = Arrays.asList("first", "second");
+        List<Integer> expectedTList = Arrays.asList(110, 111, 101);
+
+        assertEquals(expectedIntegerList, result.integerList);
+        assertEquals(expectedStringList, result.stringList);
+        assertEquals(expectedTList, result.tVar);
+    }
+
+    @Test
+    public void testAdaptObjectInCollection() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {new BoxToCrateCompatibleGenericsAdapter<Integer>() {
+        }};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo<Integer> pojo = new AdaptedPojo<>();
+
+        pojo.tGenericBoxList = new ArrayList<>();
+        pojo.tGenericBoxList.add(new GenericBox<>("GEN_BOX_STR_1", 110));
+        pojo.tGenericBoxList.add(new GenericBox<>("GEN_BOX_STR_2", 101));
+
+        String marshalledJson = jsonb.toJson(pojo, new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals("{\"tGenericBoxList\":[{\"adaptedT\":110,\"crateStrField\":\"GEN_BOX_STR_1\"},{\"adaptedT\":101,\"crateStrField\":\"GEN_BOX_STR_2\"}]}", marshalledJson);
+
+        String toUnmarshall = "{\"integerList\":[11,22,33,44],\"stringList\":[\"first\",\"second\"]," +
+                "\"tGenericBoxList\":[{\"crateStrField\":\"FirstCrate\",\"adaptedT\":11},{\"crateStrField\":\"SecondCrate\",\"adaptedT\":22}]}";
+
+        AdaptedPojo<Integer> result = jsonb.fromJson(toUnmarshall, new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals("FirstCrate", result.tGenericBoxList.get(0).getStrField());
+        assertEquals("SecondCrate", result.tGenericBoxList.get(1).getStrField());
+        assertEquals(Integer.valueOf(11), result.tGenericBoxList.get(0).getX());
+        assertEquals(Integer.valueOf(22), result.tGenericBoxList.get(1).getX());
+    }
+
+    @Test
+    public void testAdaptTypeIntoCollection() throws Exception {
+
+        JsonbAdapter<?, ?>[] adapters = {new JsonbAdapter<String, List<Integer>>() {
+            @Override
+            public List<Integer> adaptToJson(String s) {
+                List<Integer> result = new ArrayList<>();
+                for (String str : s.split(",")) {
+                    result.add(Integer.parseInt(str));
+                }
+                return result;
+            }
+
+            @Override
+            public String adaptFromJson(List<Integer> ints) {
+                StringBuilder sb = new StringBuilder();
+                for (Integer i : ints) {
+                    if (!sb.toString().isEmpty()) {
+                        sb.append(",");
+                    }
+                    sb.append(i);
+                }
+                return sb.toString();
+            }
+        }
+        };
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        String json = "{\"strValues\":[11,22,33]}";
+        final NonGenericPojo object = new NonGenericPojo();
+        object.strValues = "11,22,33";
+        assertEquals(json, jsonb.toJson(object));
+        NonGenericPojo pojo = jsonb.fromJson(json, NonGenericPojo.class);
+        assertEquals("11,22,33", pojo.strValues);
+    }
+
+    @Test
+    public void testMarshallGenericField() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {new BoxToCratePropagatedIntegerStringAdapter()};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo<Integer> adaptedPojo = new AdaptedPojo<>();
+        adaptedPojo.tBox = new GenericBox<>("tGenBoxStrField", 22);
+        adaptedPojo.intBox = new GenericBox<>("genBoxStrField", 11);
+        String json = jsonb.toJson(adaptedPojo, new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals("{\"intBox\":{\"adaptedT\":{\"x\":[\"11\"]},\"crateStrField\":\"genBoxStrField\"},\"tBox\":{\"adaptedT\":{\"x\":[\"22\"]},\"crateStrField\":\"tGenBoxStrField\"}}", json);
+
+        AdaptedPojo<Integer> unmarshalledAdaptedPojo = jsonb.fromJson(json, new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals("genBoxStrField", unmarshalledAdaptedPojo.intBox.getStrField());
+        assertEquals(Integer.valueOf(11), unmarshalledAdaptedPojo.intBox.getX());
+    }
+
+    @Test
+    public void testTypeVariable() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {new JsonbAdapter<List<GenericBox<Double>>, BigDecimal>() {
+            @Override
+            public BigDecimal adaptToJson(List<GenericBox<Double>> genericBoxes) {
+                return BigDecimal.valueOf(genericBoxes.get(0).getX());
+            }
+
+            @Override
+            public List<GenericBox<Double>> adaptFromJson(BigDecimal bigDecimal) {
+                List<GenericBox<Double>> list = new ArrayList<>();
+                list.add(new GenericBox<>("", bigDecimal.doubleValue()));
+                return list;
+            }
+        }};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo<List<GenericBox<Double>>> intBoxPojo = new AdaptedPojo<>();
+        List<GenericBox<Double>> intBoxList = new ArrayList<>();
+        intBoxList.add(new GenericBox<>("", 11d));
+        intBoxPojo.tVar = intBoxList;
+
+        String json = jsonb.toJson(intBoxPojo, new TestTypeToken<AdaptedPojo<List<GenericBox<Double>>>>(){}.getType());
+        assertEquals("{\"tVar\":11.0}", json);
+
+        AdaptedPojo<List<GenericBox<Double>>> result = jsonb.fromJson(json, new TestTypeToken<AdaptedPojo<List<GenericBox<Double>>>>(){}.getType());
+        assertEquals(Double.valueOf(11), result.tVar.get(0).getX());
+    }
+
+    @Test
+    public void testAdaptRoot() throws Exception {
+
+        JsonbAdapter<?, ?>[] adapters = {new JsonbAdapter<Box, Crate>() {
+            @Override
+            public Crate adaptToJson(Box box) {
+                return new Crate(box.getBoxStrField(), box.getBoxIntegerField());
+            }
+
+            @Override
+            public Box adaptFromJson(Crate crate) {
+                return new Box(crate.getCrateStrField(), crate.getCrateIntField());
+            }
+        }};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        Box pojo = new Box("BOX_STR", 101);
+        String marshalledJson = jsonb.toJson(pojo);
+        assertEquals("{\"crateIntField\":101,\"crateStrField\":\"BOX_STR\"}", marshalledJson);
+
+        Box result = jsonb.fromJson("{\"crateIntField\":110,\"crateStrField\":\"CRATE_STR\"}", Box.class);
+        assertEquals("CRATE_STR", result.getBoxStrField());
+        assertEquals(Integer.valueOf(110), result.getBoxIntegerField());
+    }
+
+    @Test
+    public void testAdaptMapString() throws Exception {
+
+        JsonbAdapter<?, ?>[] adapters = {new JsonbAdapter<Map<String, Integer>, String>() {
+            @Override
+            public Map<String, Integer> adaptFromJson(String obj) throws Exception {
+                final HashMap<String, Integer> result = new HashMap<>();
+                result.put("fake", 101);
+                return result;
+            }
+
+            @Override
+            public String adaptToJson(Map<String, Integer> obj) throws Exception {
+                StringBuilder sb = new StringBuilder();
+                for (Map.Entry<String, Integer> entry : obj.entrySet()) {
+                    if (sb.length() > 0) {
+                        sb.append("#");
+                    }
+                    sb.append(entry.getKey()).append("-").append(entry.getValue());
+                }
+                return sb.toString();
+            }
+        }};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo<Integer> pojo = new AdaptedPojo<>();
+        pojo.stringIntegerMap = new HashMap<>();
+        pojo.stringIntegerMap.put("first", 11);
+        pojo.stringIntegerMap.put("second", 22);
+        pojo.tMap = new HashMap<>(pojo.stringIntegerMap);
+        String marshalledJson = jsonb.toJson(pojo, new AdaptedPojo<Integer>(){}.getClass());
+        assertEquals("{\"stringIntegerMap\":\"first-11#second-22\",\"tMap\":\"first-11#second-22\"}", marshalledJson);
+
+        AdaptedPojo<Integer> result = jsonb.fromJson("{\"stringIntegerMap\":\"fake-value\",\"tMap\":\"fake-value\"}", new TestTypeToken<AdaptedPojo<Integer>>(){}.getType());
+        assertEquals(Integer.valueOf(101), result.stringIntegerMap.get("fake"));
+        assertEquals(Integer.valueOf(101), result.tMap.get("fake"));
+    }
+
+    @Test
+    public void testAdaptMapToObject() throws Exception {
+        JsonbAdapter<?, ?>[] adapters = {new JsonbAdapter<Map<String, String>, Crate>() {
+            @Override
+            public Map<String, String> adaptFromJson(Crate obj) throws Exception {
+                final HashMap<String, String> fake = new HashMap<>();
+                fake.put("fake", "11");
+                return fake;
+            }
+
+            @Override
+            public Crate adaptToJson(Map<String, String> obj) throws Exception {
+                final Map.Entry<String, String> next = obj.entrySet().iterator().next();
+                return new Crate(next.getKey(), Integer.parseInt(next.getValue()));
+            }
+        }};
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(JsonbConfig.ADAPTERS, adapters));
+
+        AdaptedPojo<String> pojo = new AdaptedPojo<>();
+        pojo.tMap = new HashMap<>();
+        pojo.tMap.put("first", "101");
+
+        TestTypeToken<AdaptedPojo<String>> typeToken = new TestTypeToken<AdaptedPojo<String>>() {};
+
+        String marshalledJson = jsonb.toJson(pojo, typeToken.getType());
+        assertEquals("{\"tMap\":{\"crateIntField\":101,\"crateStrField\":\"first\"}}", marshalledJson);
+
+        AdaptedPojo<String> result = jsonb.fromJson("{\"tMap\":{\"crateIntField\":101,\"crateStrField\":\"first\"}}", typeToken.getType());
+        assertEquals("11", result.tMap.get("fake"));
+    }
+
+    @Test
+    public void testAdaptJsonObject() {
+        JsonObjectPojo pojo = new JsonObjectPojo();
+        pojo.box = new Box("strFieldValue", 110);
+
+        String json = defaultJsonb.toJson(pojo);
+        assertEquals("{\"box\":{\"boxStrField\":\"strFieldValue\",\"boxIntegerField\":110}}", json);
+
+        JsonObjectPojo result = defaultJsonb.fromJson(json, JsonObjectPojo.class);
+        assertEquals("strFieldValue", result.box.getBoxStrField());
+        assertEquals(Integer.valueOf(110), result.box.getBoxIntegerField());
+    }
+
+    @Test
+    public void testAdaptAuthor() {
+        Author author = new Author();
+        author.setFirstName("John");
+        author.setLastName("Connor");
+
+        String json = defaultJsonb.toJson(author);
+        assertEquals("{\"firstName\":\"J\",\"lastName\":\"Connor\"}", json);
+
+        Author result = defaultJsonb.fromJson("{\"firstName\":\"J\",\"lastName\":\"Connor\"}", Author.class);
+        assertEquals("\"J\"", result.getFirstName());
+        assertEquals("Connor", result.getLastName());
+    }
+
+    @Test
+    public void testAdapterReturningNull() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withAdapters(new ReturnNullAdapter()).withNullValues(true));
+
+        ScalarValueWrapper<Number> wrapper = new ScalarValueWrapper<>();
+        wrapper.setValue(10);
+        Type type = new TestTypeToken<ScalarValueWrapper<Number>>() {
+        }.getType();
+        String json = jsonb.toJson(wrapper, type);
+
+        assertEquals("{\"value\":null}", json);
+
+        ScalarValueWrapper<Number> result = jsonb.fromJson("{\"value\":null}", type);
+        assertNull(result.getValue());
+    }
+
+    @Test
+    public void testAdaptUUID() {
+        UUIDContainer pojo = new UUIDContainer();
+        UUID uuid = UUID.fromString("b329da91-0d96-44b6-b466-56c2458b2877");
+        pojo.setUuidClsBased(uuid);
+        pojo.setUuidIfcBased(uuid);
+
+        String result = defaultJsonb.toJson(pojo);
+        assertEquals("{\"uuidClsBased\":\"b329da91-0d96-44b6-b466-56c2458b2877\",\"uuidIfcBased\":\"b329da91-0d96-44b6-b466-56c2458b2877\"}", result);
+
+        UUIDContainer uuidContainer = defaultJsonb.fromJson(result, UUIDContainer.class);
+        assertEquals(uuid, uuidContainer.getUuidClsBased());
+        assertEquals(uuid, uuidContainer.getUuidIfcBased());
+    }
+
+    @Test
+    public void testSupertypeAdapter() {
+        SupertypeAdapterPojo pojo = new SupertypeAdapterPojo();
+        pojo.setNumberInteger(10);
+        pojo.setSerializableInteger(11);
+        assertEquals("{\"numberInteger\":\"11\",\"serializableInteger\":12}", defaultJsonb.toJson(pojo));
+        pojo = defaultJsonb.fromJson("{\"numberInteger\":\"11\",\"serializableInteger\":12}", SupertypeAdapterPojo.class);
+        assertEquals(Integer.valueOf(10), pojo.getNumberInteger());
+        assertEquals(Integer.valueOf(11), pojo.getSerializableInteger());
+    }
+    
+    public static class PropertyTypeMismatch {
+        private Throwable error = new RuntimeException("foo");
+        
+        public Optional<Throwable> getError() {
+            return Optional.ofNullable(error);
+        }
+        
+        public void setError(Instant errorTime) {
+            this.error = new RuntimeException("Error at: " + errorTime.toString());
+        }
+    }        
+    
+    public static class ThrowableAdapter implements JsonbAdapter<Throwable, Map<String, Object>> {
+
+        public int callCount = 0;
+
+        @Override
+        public Map<String, Object> adaptToJson(Throwable obj) throws Exception {
+            HashMap<String, Object> output = new HashMap<>();
+            output.put("message", obj.getMessage());
+            output.put("type", obj.getClass().getName());
+            callCount++;
+
+            return unmodifiableMap(output);
+        }
+
+        @Override
+        public Throwable adaptFromJson(Map<String, Object> obj) throws Exception {
+            throw new UnsupportedOperationException("not implemented");
+        }
+    }
+    
+    /**
+     * Serialize a class that has mismatching properties. The field is of type
+     * Throwable but the getter method is of type Optional<Throwable>. The user-defined
+     * adapter for Throwable should still be called.
+     */
+    @Test
+    public void testOptionalAdapter() {
+        ThrowableAdapter adapter = new ThrowableAdapter();
+        Jsonb jsonb = JsonbBuilder.newBuilder().withConfig(new JsonbConfig().withAdapters(adapter)).build();
+        
+        PropertyTypeMismatch obj = new PropertyTypeMismatch();
+        String json = jsonb.toJson(obj);
+        assertEquals("{\"error\":{\"message\":\"foo\",\"type\":\"java.lang.RuntimeException\"}}", json);
+        assertEquals(1, adapter.callCount, "The user-defined ThrowableAdapter should have been called");
+    }
+    
+    public static class InstantAdapter implements JsonbAdapter<Instant, String> {
+
+        public int callCount = 0;
+
+        @Override
+        public String adaptToJson(Instant obj) throws Exception {
+            return obj.toString();
+        }
+
+        @Override
+        public Instant adaptFromJson(String obj) throws Exception {
+            callCount++;
+            if (obj.equals("CUSTOM_VALUE"))
+                return Instant.MAX;
+            return Instant.parse(obj);
+        }
+    }
+    
+    /**
+     * Make sure that the same property can use a different adapter for
+     * serialization and deserialization.
+     */
+    @Test
+    public void testDifferentAdapters() {
+        ThrowableAdapter throwableAdapter = new ThrowableAdapter();
+        InstantAdapter instantAdapter = new InstantAdapter();
+        Jsonb jsonb = JsonbBuilder.newBuilder()
+                .withConfig(new JsonbConfig().withAdapters(throwableAdapter, instantAdapter))
+                .build();
+        
+        String json = "{\"error\":\"CUSTOM_VALUE\"}";
+        PropertyTypeMismatch obj = jsonb.fromJson(json, PropertyTypeMismatch.class);
+        assertEquals("Error at: +1000000000-12-31T23:59:59.999999999Z", obj.getError().get().getMessage());
+        assertEquals(1, instantAdapter.callCount);
+        
+        String afterJson = jsonb.toJson(obj);
+        assertEquals("{\"error\":{\"message\":\"Error at: +1000000000-12-31T23:59:59.999999999Z\",\"type\":\"java.lang.RuntimeException\"}}", 
+                afterJson);
+        assertEquals(1, throwableAdapter.callCount);
+    }
+    
+    public static class StringAdapter implements JsonbAdapter<String, String> {
+		@Override
+		public String adaptToJson(String obj) throws Exception {
+			return obj.toUpperCase();
+		}
+
+		@Override
+		public String adaptFromJson(String obj) throws Exception {
+			return obj.toLowerCase();
+		}
+    }
+    
+    /**
+     * Test for: https://github.com/eclipse-ee4j/yasson/issues/346
+     */
+    @Test
+    public void testAdaptedRootType() {
+    	Jsonb jsonb = JsonbBuilder.newBuilder()
+    			.withConfig(new JsonbConfig().withAdapters(new StringAdapter()))
+    			.build();
+    	
+    	String original = "hello world!";
+    	assertEquals("\"HELLO WORLD!\"", jsonb.toJson(original));
+    	assertEquals(original, jsonb.fromJson("\"HELLO WORLD!\"", String.class));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java b/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java
new file mode 100644
index 0000000..df1ecbc
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/JsonbTypeAdapterTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.adapters.model.*;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbTypeAdapterTest {
+
+    public static class BoxToStringAdapter implements JsonbAdapter<Box, String> {
+
+        @Override
+        public Box adaptFromJson(String obj) throws Exception {
+            String[] strings = obj.split(":");
+            return new Box(strings[0], Integer.valueOf(strings[1]));
+        }
+
+        @Override
+        public String adaptToJson(Box obj) throws Exception {
+            return obj.getBoxStrField()+":"+obj.getBoxIntegerField();
+        }
+    }
+
+    public static class IncompatibleAdapterPojo<T,X> {
+
+        @JsonbTypeAdapter(BoxToStringAdapter.class)
+        public String str;
+
+    }
+
+    public static class AnnotatedPojo<T,X> {
+
+        @JsonbTypeAdapter(BoxToStringAdapter.class)
+        public Box box;
+        @JsonbTypeAdapter(BoxToCratePropagatedIntegerStringAdapter.class)
+        public GenericBox<T> tBox;
+        @JsonbTypeAdapter(BoxToCratePropagatedIntegerStringAdapter.class)
+        public GenericBox<X> xBox;
+    }
+
+    @Test
+    public void testIncompatibleAdapter() throws Exception {
+        IncompatibleAdapterPojo incompatibleAdapterFieldPojo = new IncompatibleAdapterPojo();
+        incompatibleAdapterFieldPojo.str = "STR";
+        try {
+            defaultJsonb.toJson(incompatibleAdapterFieldPojo);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("Adapter of runtime type class"));
+            assertTrue(e.getMessage().contains("does not match property type "));
+        }
+    }
+
+    @Test
+    public void testGenericFieldsMatch() {
+        AnnotatedPojo<Integer, String> annotatedPojo = new AnnotatedPojo<>();
+        annotatedPojo.tBox = new GenericBox<>("T_BOX", 110);
+        String marshalledJson = defaultJsonb.toJson(annotatedPojo, new TestTypeToken<AnnotatedPojo<Integer, String>>(){}.getType());
+        assertEquals("{\"tBox\":{\"adaptedT\":{\"x\":[\"110\"]},\"crateStrField\":\"T_BOX\"}}", marshalledJson);
+
+        AnnotatedPojo<Integer,String> result = defaultJsonb.fromJson("{\"tBox\":{\"crateStrField\":\"T_BOX\",\"adaptedT\":{\"x\":[\"110\"]}}}", new TestTypeToken<AnnotatedPojo<Integer,String>>(){}.getType());
+        assertEquals("T_BOX", result.tBox.getStrField());
+        assertEquals(Integer.valueOf(110), result.tBox.getX());
+    }
+
+    @Test
+    public void testAnnotatedTbox() throws Exception {
+        AnnotatedPojo pojo = new AnnotatedPojo();
+        pojo.box = new Box("STR", 101);
+        String marshalledJson = defaultJsonb.toJson(pojo);
+        assertEquals("{\"box\":\"STR:101\"}", marshalledJson);
+
+        AnnotatedPojo<?, ?> result = defaultJsonb.fromJson("{\"box\":\"STR:110\"}", AnnotatedPojo.class);
+        assertEquals("STR", result.box.getBoxStrField());
+        assertEquals(Integer.valueOf(110), result.box.getBoxIntegerField());
+    }
+
+    @Test
+    public void testBoxWithTypeAdapter() {
+        BoxWithAdapter boxWithAdapter = new BoxWithAdapter("STR", 101);
+        String marshalledJson = defaultJsonb.toJson(boxWithAdapter);
+        assertEquals("{\"boxInteger\":101,\"boxStr\":\"STR\"}", marshalledJson);
+
+        BoxWithAdapter result = defaultJsonb.fromJson("{\"boxInteger\":101,\"boxStr\":\"STR\"}", BoxWithAdapter.class);
+        assertEquals("STR", result.getBoxStrField());
+        assertEquals(Integer.valueOf(101), result.getBoxIntegerField());
+    }
+
+    @Test
+    public void testBoxWithTypeSerializer() {
+        BoxWithSerializer boxWithSerializer = new BoxWithSerializer("STR", 101);
+        String marshalledJson = defaultJsonb.toJson(boxWithSerializer);
+        assertEquals("{\"boxInteger\":101,\"boxStr\":\"STR\"}", marshalledJson);
+    }
+
+    @Test
+    public void testBoxWithTypeDeserializer() {
+        BoxWithDeserializer result = defaultJsonb.fromJson("{\"boxInteger\":101,\"boxStr\":\"STR\"}", BoxWithDeserializer.class);
+        assertEquals("STR", result.getBoxStrField());
+        assertEquals(Integer.valueOf(101), result.getBoxIntegerField());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/AdaptedPojo.java b/src/test/java/org/eclipse/yasson/adapters/model/AdaptedPojo.java
new file mode 100644
index 0000000..1b01df2
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/AdaptedPojo.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class AdaptedPojo<T> {
+    public String strField;
+    public Integer intField;
+    public Box box;
+    public GenericBox<Integer> intBox;
+    public GenericBox<String> strBox;
+    public GenericBox<T> tBox;
+    public List<GenericBox<T>> tGenericBoxList;
+    public List<Integer> integerList;
+    public List<String> stringList;
+    public Map<String, Integer> stringIntegerMap;
+    public Map<String, T> tMap;
+    public T tVar;
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/Author.java b/src/test/java/org/eclipse/yasson/adapters/model/Author.java
new file mode 100644
index 0000000..e737dc6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/Author.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+
+public class Author {
+    @JsonbTypeAdapter(FirstNameAdapter.class)
+    private String firstName;
+    private String lastName;
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/Box.java b/src/test/java/org/eclipse/yasson/adapters/model/Box.java
new file mode 100644
index 0000000..f12d981
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/Box.java
@@ -0,0 +1,47 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class Box {
+
+    private String boxStrField;
+
+    private Integer boxIntegerField;
+
+    public Box() {
+    }
+
+    public Box(String boxStrField, Integer boxIntegerField) {
+        this.boxStrField = boxStrField;
+        this.boxIntegerField = boxIntegerField;
+    }
+
+    public String getBoxStrField() {
+        return boxStrField;
+    }
+
+    public void setBoxStrField(String boxStrField) {
+        this.boxStrField = boxStrField;
+    }
+
+    public Integer getBoxIntegerField() {
+        return boxIntegerField;
+    }
+
+    public void setBoxIntegerField(Integer boxIntegerField) {
+        this.boxIntegerField = boxIntegerField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxToCrateCompatibleGenericsAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxToCrateCompatibleGenericsAdapter.java
new file mode 100644
index 0000000..373184a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxToCrateCompatibleGenericsAdapter.java
@@ -0,0 +1,37 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public abstract class BoxToCrateCompatibleGenericsAdapter<T> implements JsonbAdapter<GenericBox<T>,GenericCrate<T>> {
+
+    @Override
+    public GenericCrate<T> adaptToJson(GenericBox<T> tGenericBox) {
+        GenericCrate<T> genericCrate = new GenericCrate<>();
+        genericCrate.setCrateStrField(tGenericBox.getStrField());
+        genericCrate.setT(tGenericBox.getX());
+        return genericCrate;
+    }
+
+    @Override
+    public GenericBox<T> adaptFromJson(GenericCrate<T> tGenericCrate) {
+        GenericBox<T> genericBox = new GenericBox<>();
+        genericBox.setStrField(tGenericCrate.getCrateStrField());
+        genericBox.setX(tGenericCrate.getT());
+        return genericBox;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxToCratePropagatedIntegerStringAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxToCratePropagatedIntegerStringAdapter.java
new file mode 100644
index 0000000..4b0219c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxToCratePropagatedIntegerStringAdapter.java
@@ -0,0 +1,45 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class BoxToCratePropagatedIntegerStringAdapter extends BoxToCratePropagatedTypeArgsAdapter<Integer, String> {
+    @Override
+    public GenericCrate<GenericBox<List<String>>> adaptToJson(GenericBox<Integer> integerGenericBox) {
+        if (integerGenericBox == null) {
+            return null;
+        }
+        GenericCrate<GenericBox<List<String>>> crate = new GenericCrate<>();
+        crate.setCrateStrField(integerGenericBox.getStrField());
+        crate.setT(new GenericBox<>());
+        crate.getT().setX(new ArrayList<>());
+        crate.getT().getX().add(integerGenericBox.getX().toString());
+        return crate;
+    }
+
+    @Override
+    public GenericBox<Integer> adaptFromJson(GenericCrate<GenericBox<List<String>>> boxGenericCrate) {
+        if (boxGenericCrate == null) {
+            return null;
+        }
+        GenericBox<Integer> genericBox = new GenericBox<>();
+        genericBox.setStrField(boxGenericCrate.getCrateStrField());
+        genericBox.setX(Integer.parseInt(boxGenericCrate.getT().getX().get(0)));
+        return genericBox;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxToCratePropagatedTypeArgsAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxToCratePropagatedTypeArgsAdapter.java
new file mode 100644
index 0000000..dde82cb
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxToCratePropagatedTypeArgsAdapter.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+import java.util.List;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public abstract class BoxToCratePropagatedTypeArgsAdapter<T,X> implements JsonbAdapter<GenericBox<T>, GenericCrate<GenericBox<List<X>>>> {
+
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxToJsonObjectAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxToJsonObjectAdapter.java
new file mode 100644
index 0000000..a906a68
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxToJsonObjectAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import jakarta.json.spi.JsonProvider;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class BoxToJsonObjectAdapter implements JsonbAdapter<Box, JsonObject> {
+    @Override
+    public JsonObject adaptToJson(Box obj) throws Exception {
+        final JsonObjectBuilder builder = JsonProvider.provider().createObjectBuilder();
+        builder.add("boxStrField", obj.getBoxStrField());
+        builder.add("boxIntegerField", obj.getBoxIntegerField());
+        return builder.build();
+    }
+
+    @Override
+    public Box adaptFromJson(JsonObject jsonObj) throws Exception {
+        Box box = new Box();
+        box.setBoxStrField(jsonObj.getString("boxStrField"));
+        box.setBoxIntegerField(jsonObj.getInt("boxIntegerField"));
+        return box;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxWithAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithAdapter.java
new file mode 100644
index 0000000..1a3d7d6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithAdapter.java
@@ -0,0 +1,50 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+
+/**
+ * @author David Kral
+ */
+@JsonbTypeAdapter(BoxWithAdapterAdapter.class)
+public class BoxWithAdapter {
+
+    private String boxStrField;
+
+    private Integer boxIntegerField;
+
+    public BoxWithAdapter() {
+    }
+
+    public BoxWithAdapter(String boxStrField, Integer boxIntegerField) {
+        this.boxStrField = boxStrField;
+        this.boxIntegerField = boxIntegerField;
+    }
+
+    public String getBoxStrField() {
+        return boxStrField;
+    }
+
+    public void setBoxStrField(String boxStrField) {
+        this.boxStrField = boxStrField;
+    }
+
+    public Integer getBoxIntegerField() {
+        return boxIntegerField;
+    }
+
+    public void setBoxIntegerField(Integer boxIntegerField) {
+        this.boxIntegerField = boxIntegerField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxWithAdapterAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithAdapterAdapter.java
new file mode 100644
index 0000000..0ebf6f1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithAdapterAdapter.java
@@ -0,0 +1,39 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import jakarta.json.spi.JsonProvider;
+
+/**
+ * @author David Kral
+ */
+public class BoxWithAdapterAdapter implements JsonbAdapter<BoxWithAdapter, JsonObject> {
+    @Override
+    public JsonObject adaptToJson(BoxWithAdapter obj) {
+        final JsonObjectBuilder builder = JsonProvider.provider().createObjectBuilder();
+        builder.add("boxInteger", obj.getBoxIntegerField());
+        builder.add("boxStr", obj.getBoxStrField());
+        return builder.build();
+    }
+
+    @Override
+    public BoxWithAdapter adaptFromJson(JsonObject jsonObj) {
+        BoxWithAdapter box = new BoxWithAdapter();
+        box.setBoxIntegerField(jsonObj.getInt("boxInteger"));
+        box.setBoxStrField(jsonObj.getString("boxStr"));
+        return box;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxWithDeserializer.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithDeserializer.java
new file mode 100644
index 0000000..34cac00
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithDeserializer.java
@@ -0,0 +1,50 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+
+/**
+ * @author David Kral
+ */
+@JsonbTypeDeserializer(BoxWithDeserializerDeserializer.class)
+public class BoxWithDeserializer {
+
+    private String boxStrField;
+
+    private Integer boxIntegerField;
+
+    public BoxWithDeserializer() {
+    }
+
+    public BoxWithDeserializer(String boxStrField, Integer boxIntegerField) {
+        this.boxStrField = boxStrField;
+        this.boxIntegerField = boxIntegerField;
+    }
+
+    public String getBoxStrField() {
+        return boxStrField;
+    }
+
+    public void setBoxStrField(String boxStrField) {
+        this.boxStrField = boxStrField;
+    }
+
+    public Integer getBoxIntegerField() {
+        return boxIntegerField;
+    }
+
+    public void setBoxIntegerField(Integer boxIntegerField) {
+        this.boxIntegerField = boxIntegerField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxWithDeserializerDeserializer.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithDeserializerDeserializer.java
new file mode 100644
index 0000000..86103da
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithDeserializerDeserializer.java
@@ -0,0 +1,38 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+
+/**
+ * @author David Kral
+ */
+public class BoxWithDeserializerDeserializer implements JsonbDeserializer<BoxWithDeserializer> {
+
+    @Override
+    public BoxWithDeserializer deserialize(JsonParser jsonParser, DeserializationContext ctx, Type type) {
+        BoxWithDeserializer box = new BoxWithDeserializer();
+        while (jsonParser.hasNext()) {
+            JsonParser.Event next = jsonParser.next();
+            if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("boxInteger")) {
+                box.setBoxIntegerField(ctx.deserialize(Integer.class, jsonParser));
+            } else if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("boxStr")) {
+                box.setBoxStrField(ctx.deserialize(String.class, jsonParser));
+            }
+        }
+        return box;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxWithSerializer.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithSerializer.java
new file mode 100644
index 0000000..450c62e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithSerializer.java
@@ -0,0 +1,50 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+
+/**
+ * @author David Kral
+ */
+@JsonbTypeSerializer(BoxWithSerializerSerializer.class)
+public class BoxWithSerializer {
+
+    private String boxStrField;
+
+    private Integer boxIntegerField;
+
+    public BoxWithSerializer() {
+    }
+
+    public BoxWithSerializer(String boxStrField, Integer boxIntegerField) {
+        this.boxStrField = boxStrField;
+        this.boxIntegerField = boxIntegerField;
+    }
+
+    public String getBoxStrField() {
+        return boxStrField;
+    }
+
+    public void setBoxStrField(String boxStrField) {
+        this.boxStrField = boxStrField;
+    }
+
+    public Integer getBoxIntegerField() {
+        return boxIntegerField;
+    }
+
+    public void setBoxIntegerField(Integer boxIntegerField) {
+        this.boxIntegerField = boxIntegerField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/BoxWithSerializerSerializer.java b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithSerializerSerializer.java
new file mode 100644
index 0000000..85ceb96
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/BoxWithSerializerSerializer.java
@@ -0,0 +1,32 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * @author David Kral
+ */
+public class BoxWithSerializerSerializer implements JsonbSerializer<BoxWithSerializer> {
+
+    @Override
+    public void serialize(BoxWithSerializer boxWithSerializer, JsonGenerator jsonGenerator, SerializationContext serializationContext) {
+        jsonGenerator
+                .writeStartObject()
+                .writeKey("boxInteger").write(boxWithSerializer.getBoxIntegerField())
+                .writeKey("boxStr").write(boxWithSerializer.getBoxStrField())
+                .writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/Chain.java b/src/test/java/org/eclipse/yasson/adapters/model/Chain.java
new file mode 100644
index 0000000..5b53df5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/Chain.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+public class Chain {
+    
+    private String name;
+    private Chain linksTo;
+    private Foo has;
+    
+    public Chain(String name) {
+        this.name = name;
+    }
+    
+    public Chain() {
+    }
+
+    public String getName() {
+        return name;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+    public Chain getLinksTo() {
+        return linksTo;
+    }
+    public void setLinksTo(Chain linksTo) {
+        this.linksTo = linksTo;
+    }
+    public Foo getHas() {
+        return has;
+    }
+    public void setHas(Foo has) {
+        this.has = has;
+    }
+    
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/ChainAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/ChainAdapter.java
new file mode 100644
index 0000000..1b72679
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/ChainAdapter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+public class ChainAdapter implements JsonbAdapter<Chain, Map<String, Object>>{
+
+    @Override
+    public Map<String, Object> adaptToJson(Chain obj) throws Exception {
+        Map<String, Object> map = new LinkedHashMap<>();
+        map.put("has", obj.getHas());
+        map.put("linksTo", obj.getLinksTo());
+        map.put("name", obj.getName());
+        return map;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Chain adaptFromJson(Map<String, Object> obj) throws Exception {
+        if(obj != null) {
+            Chain chain = new Chain((String) obj.get("name"));
+            chain.setHas((Foo) obj.get("has"));
+            adaptFromJson((Map<String, Object>) obj.get("linksTo"));
+            return chain;
+        } else {
+            return null;
+        }
+        
+    }
+    
+}
\ No newline at end of file
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/ChainSerializer.java b/src/test/java/org/eclipse/yasson/adapters/model/ChainSerializer.java
new file mode 100644
index 0000000..c727fd3
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/ChainSerializer.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+public class ChainSerializer implements JsonbSerializer<Chain>{
+    
+    public static final String RECURSIVE_REFERENCE_ERROR = "There is a recursive reference";
+    
+    @Override
+    public void serialize(Chain obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        if(obj.getHas() != null) {
+            ctx.serialize("has", obj.getHas(), generator);
+        }
+        if(obj.getLinksTo() != null) {
+            ctx.serialize("linksTo", obj.getLinksTo(), generator);
+        }
+        generator.write("name", obj.getName());
+        generator.writeEnd();
+    }
+    
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/Crate.java b/src/test/java/org/eclipse/yasson/adapters/model/Crate.java
new file mode 100644
index 0000000..daebcd1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/Crate.java
@@ -0,0 +1,47 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class Crate {
+
+    private String crateStrField;
+
+    private Integer crateIntField;
+
+    public Crate() {
+    }
+
+    public Crate(String crateStrField, Integer crateIntField) {
+        this.crateStrField = crateStrField;
+        this.crateIntField = crateIntField;
+    }
+
+    public String getCrateStrField() {
+        return crateStrField;
+    }
+
+    public void setCrateStrField(String crateStrField) {
+        this.crateStrField = crateStrField;
+    }
+
+    public Integer getCrateIntField() {
+        return crateIntField;
+    }
+
+    public void setCrateIntField(Integer crateIntField) {
+        this.crateIntField = crateIntField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/FirstNameAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/FirstNameAdapter.java
new file mode 100644
index 0000000..e12bace
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/FirstNameAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.Json;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+public class FirstNameAdapter implements JsonbAdapter<String, JsonValue> {
+    @Override
+    public JsonValue adaptToJson(String firstName) {
+        return Json.createValue(firstName.subSequence(0,1).toString());
+    }
+    @Override
+    public String adaptFromJson(JsonValue json) {
+        return json.toString();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/Foo.java b/src/test/java/org/eclipse/yasson/adapters/model/Foo.java
new file mode 100644
index 0000000..932159c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/Foo.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+public class Foo {
+    
+    private String bar;
+
+    public Foo(String bar) {
+        this.bar = bar;
+    }
+
+    public String getBar() {
+        return bar;
+    }
+
+    public void setBar(String bar) {
+        this.bar = bar;
+    }
+    
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/FooAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/FooAdapter.java
new file mode 100644
index 0000000..4fab980
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/FooAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+public class FooAdapter implements JsonbAdapter<Foo, Map<String, String>>{
+
+    @Override
+    public Map<String, String> adaptToJson(Foo obj) throws Exception {
+        Map<String, String> map = new HashMap<>();
+        map.put("bar", obj.getBar());
+        return map;
+    }
+
+    @Override
+    public Foo adaptFromJson( Map<String, String> obj) throws Exception {
+        return new Foo(obj.get("bar").toString());
+    }
+    
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/FooSerializer.java b/src/test/java/org/eclipse/yasson/adapters/model/FooSerializer.java
new file mode 100644
index 0000000..7d5cb33
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/FooSerializer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+public class FooSerializer implements JsonbSerializer<Foo>{
+    
+    @Override
+    public void serialize(Foo obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.write("bar", obj.getBar());
+        generator.writeEnd(); 
+    }
+    
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/GenericBox.java b/src/test/java/org/eclipse/yasson/adapters/model/GenericBox.java
new file mode 100644
index 0000000..a6f19c9
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/GenericBox.java
@@ -0,0 +1,47 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class GenericBox<X> {
+
+    private String strField;
+
+    private X x;
+
+    public GenericBox(String strField, X x) {
+        this.strField = strField;
+        this.x = x;
+    }
+
+    public GenericBox() {
+    }
+
+    public X getX() {
+        return x;
+    }
+
+    public String getStrField() {
+        return strField;
+    }
+
+    public void setStrField(String strField) {
+        this.strField = strField;
+    }
+
+    public void setX(X x) {
+        this.x = x;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/GenericCrate.java b/src/test/java/org/eclipse/yasson/adapters/model/GenericCrate.java
new file mode 100644
index 0000000..c386cb5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/GenericCrate.java
@@ -0,0 +1,50 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class GenericCrate<T> {
+
+    private String crateStrField;
+
+    @JsonbProperty("adaptedT")
+    private T t;
+
+    public GenericCrate(String crateStrField, T t) {
+        this.crateStrField = crateStrField;
+        this.t = t;
+    }
+
+    public GenericCrate() {
+    }
+
+    public String getCrateStrField() {
+        return crateStrField;
+    }
+
+    public T getT() {
+        return t;
+    }
+
+    public void setCrateStrField(String crateStrField) {
+        this.crateStrField = crateStrField;
+    }
+
+    public void setT(T t) {
+        this.t = t;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/IntegerListToStringAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/IntegerListToStringAdapter.java
new file mode 100644
index 0000000..134cf37
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/IntegerListToStringAdapter.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class IntegerListToStringAdapter implements JsonbAdapter<List<Integer>, String> {
+
+    @Override
+    public String adaptToJson(List<Integer> integers) {
+        StringBuilder sb = new StringBuilder();
+        for (Integer integer : integers) {
+            if (!sb.toString().isEmpty()) {
+                sb.append("#");
+            }
+            sb.append(integer);
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public List<Integer> adaptFromJson(String s) {
+        String[] items = s.split("#");
+        List<Integer> integerList = new ArrayList<>();
+        for (String item : items) {
+            integerList.add(Integer.parseInt(item));
+        }
+        return integerList;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/JsonObjectPojo.java b/src/test/java/org/eclipse/yasson/adapters/model/JsonObjectPojo.java
new file mode 100644
index 0000000..15b9bf8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/JsonObjectPojo.java
@@ -0,0 +1,26 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonObjectPojo {
+
+    @JsonbTypeAdapter(BoxToJsonObjectAdapter.class)
+    public Box box;
+
+    public GenericBox<Box> genericBox;
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/LocalPolymorphicAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/LocalPolymorphicAdapter.java
new file mode 100644
index 0000000..c77530b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/LocalPolymorphicAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+import java.lang.reflect.Constructor;
+import java.util.stream.Stream;
+
+/**
+ * Causes {@link StackOverflowError} if recursive calls of user components are not checked by runtime.
+ */
+public abstract class LocalPolymorphicAdapter<T> implements JsonbAdapter<T, LocalTypeWrapper<T>> {
+
+    private final String[] allowedClasses;
+
+    /**
+     * Create new instance.
+     *
+     * @param allowedClasses allowed classes for loading by name
+     */
+    public LocalPolymorphicAdapter(final Class... allowedClasses) {
+        this.allowedClasses = Stream.of(allowedClasses).map(Class::getName).toArray(value -> new String[allowedClasses.length]);
+    }
+
+    /**
+     * Returns all classes which are allowed for loading.
+     *
+     * @return allowed classes for loading by name
+     */
+    public String[] getAllowedClasses() {
+        return allowedClasses;
+    }
+
+    @Override
+    public LocalTypeWrapper<T> adaptToJson(T obj) throws Exception {
+        LocalTypeWrapper<T> wrapper = new LocalTypeWrapper<>();
+        wrapper.setClassName(obj.getClass().getName());
+        wrapper.setInstance(obj);
+        return wrapper;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final T adaptFromJson(LocalTypeWrapper<T> obj) throws Exception {
+        if (!isAllowed(obj.getClassName())) {
+            throw new ClassNotFoundException(obj.getClassName());
+        }
+        Constructor<T> constructor = (Constructor<T>) Class.forName(obj.getClassName()).getConstructor();
+        T instance = constructor.newInstance();
+        populateInstance(instance, obj);
+        return instance;
+    }
+
+    protected abstract void populateInstance(T instance, LocalTypeWrapper<T> obj);
+
+    private boolean isAllowed(String name) {
+        for (String className : allowedClasses) {
+            if (className.equals(name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/LocalTypeWrapper.java b/src/test/java/org/eclipse/yasson/adapters/model/LocalTypeWrapper.java
new file mode 100644
index 0000000..e00450a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/LocalTypeWrapper.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+/**
+ * Created by Roman Grigoriadi (roman.grigoriadi@oracle.com) on 08/06/2017.
+ */
+public class LocalTypeWrapper<E> {
+    private String className;
+    private E instance;
+
+    /**
+     * Gets class name.
+     *
+     * @return Class name.
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    /**
+     * Sets class name.
+     *
+     * @param className Class name to set.
+     */
+    public void setClassName(String className) {
+        this.className = className;
+    }
+
+    /**
+     * Gets instance.
+     *
+     * @return Instance.
+     */
+    public E getInstance() {
+        return instance;
+    }
+
+    /**
+     * Sets instance.
+     *
+     * @param instance Instance to set.
+     */
+    public void setInstance(E instance) {
+        this.instance = instance;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/MultiinterfaceAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/MultiinterfaceAdapter.java
new file mode 100644
index 0000000..3371d4f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/MultiinterfaceAdapter.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+import java.io.Serializable;
+
+interface MultiinterfaceAdapter<X, T> extends Serializable, JsonbAdapter<X, T> {
+
+
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/MultilevelAdapterClass.java b/src/test/java/org/eclipse/yasson/adapters/model/MultilevelAdapterClass.java
new file mode 100644
index 0000000..70a6211
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/MultilevelAdapterClass.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+public abstract class MultilevelAdapterClass<X, T> implements MultiinterfaceAdapter<X, T> {
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/NumberAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/NumberAdapter.java
new file mode 100644
index 0000000..2439807
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/NumberAdapter.java
@@ -0,0 +1,27 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+public class NumberAdapter implements JsonbAdapter<Number, String> {
+    @Override
+    public String adaptToJson(Number obj) throws Exception {
+        return Integer.valueOf(((Integer)obj) + 1).toString();
+    }
+
+    @Override
+    public Number adaptFromJson(String obj) throws Exception {
+        return Integer.parseInt(obj) - 1;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/ReturnNullAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/ReturnNullAdapter.java
new file mode 100644
index 0000000..0b1843e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/ReturnNullAdapter.java
@@ -0,0 +1,27 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+public class ReturnNullAdapter implements JsonbAdapter<Number, String> {
+    @Override
+    public String adaptToJson(Number obj) throws Exception {
+        return null;
+    }
+
+    @Override
+    public Number adaptFromJson(String obj) throws Exception {
+        return null;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/SerializableAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/SerializableAdapter.java
new file mode 100644
index 0000000..af8043d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/SerializableAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+import java.io.Serializable;
+
+public class SerializableAdapter implements JsonbAdapter<Serializable, Integer> {
+    @Override
+    public Integer adaptToJson(Serializable obj) throws Exception {
+        return Integer.valueOf(obj.toString()) + 1;
+    }
+
+    @Override
+    public Serializable adaptFromJson(Integer obj) throws Exception {
+        return obj - 1;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/SupertypeAdapterPojo.java b/src/test/java/org/eclipse/yasson/adapters/model/SupertypeAdapterPojo.java
new file mode 100644
index 0000000..a79c164
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/SupertypeAdapterPojo.java
@@ -0,0 +1,40 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+
+public class SupertypeAdapterPojo {
+
+    @JsonbTypeAdapter(NumberAdapter.class)
+    private Integer numberInteger;
+
+    @JsonbTypeAdapter(SerializableAdapter.class)
+    private Integer serializableInteger;
+
+    public Integer getNumberInteger() {
+        return numberInteger;
+    }
+
+    public void setNumberInteger(Integer numberInteger) {
+        this.numberInteger = numberInteger;
+    }
+
+    public Integer getSerializableInteger() {
+        return serializableInteger;
+    }
+
+    public void setSerializableInteger(Integer serializableInteger) {
+        this.serializableInteger = serializableInteger;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/UUIDContainer.java b/src/test/java/org/eclipse/yasson/adapters/model/UUIDContainer.java
new file mode 100644
index 0000000..cd59abe
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/UUIDContainer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+import java.util.UUID;
+
+
+public class UUIDContainer {
+    @JsonbTypeAdapter(UUIDMapperClsBased.class)
+    private UUID uuidClsBased;
+
+    @JsonbTypeAdapter(UUIDMapperIfcBased.class)
+    private UUID uuidIfcBased;
+
+    public UUID getUuidClsBased() {
+        return uuidClsBased;
+    }
+
+    public void setUuidClsBased(UUID uuidClsBased) {
+        this.uuidClsBased = uuidClsBased;
+    }
+
+    public UUID getUuidIfcBased() {
+        return uuidIfcBased;
+    }
+
+    public void setUuidIfcBased(UUID uuidIfcBased) {
+        this.uuidIfcBased = uuidIfcBased;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperClsBased.java b/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperClsBased.java
new file mode 100644
index 0000000..cc7a4e5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperClsBased.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import java.util.Optional;
+import java.util.UUID;
+
+public class UUIDMapperClsBased extends MultilevelAdapterClass<UUID, String> {
+
+    @Override
+    public String adaptToJson(UUID obj) throws Exception {
+        return Optional.ofNullable(obj).map(UUID::toString).orElse(null);
+    }
+
+    @Override
+    public UUID adaptFromJson(String obj) throws Exception {
+        return Optional.ofNullable(obj).map(UUID::fromString).orElse(null);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperIfcBased.java b/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperIfcBased.java
new file mode 100644
index 0000000..62e99d8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/adapters/model/UUIDMapperIfcBased.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.adapters.model;
+
+import java.util.Optional;
+import java.util.UUID;
+
+public class UUIDMapperIfcBased implements MultiinterfaceAdapter<UUID, String> {
+
+    @Override
+    public String adaptToJson(UUID obj) throws Exception {
+        return Optional.ofNullable(obj).map(UUID::toString).orElse(null);
+    }
+
+    @Override
+    public UUID adaptFromJson(String obj) throws Exception {
+        return Optional.ofNullable(obj).map(UUID::fromString).orElse(null);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/AnnotationInheritanceTest.java b/src/test/java/org/eclipse/yasson/customization/AnnotationInheritanceTest.java
new file mode 100644
index 0000000..653efeb
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/AnnotationInheritanceTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.customization.model.InheritedAnnotationsPojo;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class AnnotationInheritanceTest {
+
+    @Test
+    public void testAnnotationInheritance() {
+        InheritedAnnotationsPojo pojo = new InheritedAnnotationsPojo();
+        String expectedJson = "{}";
+        assertEquals(expectedJson, defaultJsonb.toJson(pojo));
+
+        InheritedAnnotationsPojo result = defaultJsonb.fromJson("{\"renamedProperty\":\"abc\"}", InheritedAnnotationsPojo.class);
+        assertEquals("abc", result.property);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/EncodingTest.java b/src/test/java/org/eclipse/yasson/customization/EncodingTest.java
new file mode 100644
index 0000000..a47bf63
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/EncodingTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.TestTypeToken;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.logging.Logger;
+
+/**
+ * Tests encoding to JSONP propagation
+ *
+ * @author Roman Grigoriadi
+ */
+public class EncodingTest {
+
+    private static final Logger logger = Logger.getLogger(EncodingTest.class.getName());
+
+    private static final String[] CZECH;
+    private static final String[] RUSSIAN;
+
+
+    static {
+        ResourceBundle messages = ResourceBundle.getBundle("yasson-messages");
+        CZECH = messages.getString("czechDiacritics").split(",");
+        RUSSIAN = messages.getString("russianDiacritics").split(",");
+    }
+
+    @Test
+    public void testCP1250Encoding() throws UnsupportedEncodingException {
+    	String encoding = "cp1250";
+    	Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding));
+    	
+        testMarshaller(CZECH, jsonb, encoding);
+        testUnmarshaller(CZECH, jsonb, encoding);
+    }
+
+    @Test
+    public void testUTF8Encoding() throws UnsupportedEncodingException {
+    	String encoding = "UTF-8";
+    	Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding));
+    	
+        testMarshaller(CZECH, jsonb, encoding);
+        testUnmarshaller(CZECH, jsonb, encoding);
+        testMarshaller(RUSSIAN, jsonb, encoding);
+        testUnmarshaller(RUSSIAN, jsonb, encoding);
+    }
+
+    @Test
+    public void testcp1251Encoding() throws UnsupportedEncodingException {
+    	String encoding = "cp1251";
+    	Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withEncoding(encoding));
+    	
+        testMarshaller(RUSSIAN, jsonb, encoding);
+        testUnmarshaller(RUSSIAN, jsonb, encoding);
+    }
+
+    private static void testMarshaller(String[] input, Jsonb jsonb, String encoding) throws UnsupportedEncodingException {
+        List<String> strings = Arrays.asList(input);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        jsonb.toJson(strings, baos);
+        String marshallerResult = baos.toString(encoding);
+        logger.finest("Marshaller JSON result: "+marshallerResult);
+        assertEquals(diacriticsToJsonArray(input), marshallerResult);
+    }
+
+    private static void testUnmarshaller(String[] input, Jsonb jsonb, String encoding) throws UnsupportedEncodingException {
+        String json = diacriticsToJsonArray(input);
+        logger.finest("JSON for unmarshaller: "+json);
+        InputStream bis = new ByteArrayInputStream(json.getBytes(encoding));
+        ArrayList<String> result = jsonb.fromJson(bis, new TestTypeToken<ArrayList<String>>(){}.getType());
+        assertArrayEquals(input, result.toArray(new String[result.size()]));
+    }
+
+    private static String diacriticsToJsonArray(String[] diacritics) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        for (String str : diacritics) {
+            sb.append("\"").append(str).append("\",");
+        }
+        sb.deleteCharAt(sb.length() - 1);
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/ImplementationClassTest.java b/src/test/java/org/eclipse/yasson/customization/ImplementationClassTest.java
new file mode 100644
index 0000000..51f0e7c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/ImplementationClassTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.customization.model.Animal;
+import org.eclipse.yasson.customization.model.Dog;
+import org.eclipse.yasson.customization.model.ImplementationClassPojo;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import java.util.HashMap;
+
+import static org.eclipse.yasson.YassonConfig.USER_TYPE_MAPPING;
+
+public class ImplementationClassTest {
+
+    @Test
+    public void testAnnotatedImplementation() {
+        ImplementationClassPojo pojo = new ImplementationClassPojo();
+        Animal dog = new Dog("Bulldog");
+        pojo.setAnimal(dog);
+        String expected = "{\"animal\":{\"dogProperty\":\"Bulldog\"}}";
+        String json = defaultJsonb.toJson(pojo);
+
+        assertEquals(expected, json);
+        ImplementationClassPojo result = defaultJsonb.fromJson(expected, ImplementationClassPojo.class);
+        assertTrue(result.getAnimal() instanceof Dog);
+        assertEquals("Bulldog", ((Dog)result.getAnimal()).getDogProperty());
+    }
+
+    @Test
+    public void testJsonbConfigUserImplementation() {
+        HashMap<Class, Class> userMapping = new HashMap<>();
+        userMapping.put(Animal.class, Dog.class);
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty(USER_TYPE_MAPPING, userMapping));
+        Animal animal = new Dog("Bulldog");
+        String expected = "{\"dogProperty\":\"Bulldog\"}";
+        String json = jsonb.toJson(animal);
+
+        assertEquals(expected, json);
+
+        Dog result = (Dog) jsonb.fromJson("{\"dogProperty\":\"Bulldog\"}", Animal.class);
+        assertEquals("Bulldog", result.getDogProperty());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/InterfaceAnnotationsTest.java b/src/test/java/org/eclipse/yasson/customization/InterfaceAnnotationsTest.java
new file mode 100644
index 0000000..8022bc9
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/InterfaceAnnotationsTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.customization.model.InterfacedPojoB;
+import org.eclipse.yasson.customization.model.InterfacedPojoImpl;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class InterfaceAnnotationsTest {
+
+    @Test
+    public void testJsonbPropertyIfcInheritance() {
+        InterfacedPojoB pojo = new InterfacedPojoImpl();
+        pojo.setPropertyA("AA");
+        pojo.setPropertyB("BB");
+
+        final String json = "{\"propA\":\"AA\",\"propB\":\"BB\"}";
+        assertEquals(json, defaultJsonb.toJson(pojo));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbCreatorTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbCreatorTest.java
new file mode 100644
index 0000000..de4c44a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/JsonbCreatorTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.Set;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.bind.annotation.JsonbNumberFormat;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+import org.eclipse.yasson.customization.model.CreatorConstructorPojo;
+import org.eclipse.yasson.customization.model.CreatorFactoryMethodPojo;
+import org.eclipse.yasson.customization.model.CreatorIncompatibleTypePojo;
+import org.eclipse.yasson.customization.model.CreatorMultipleDeclarationErrorPojo;
+import org.eclipse.yasson.customization.model.CreatorPackagePrivateConstructor;
+import org.eclipse.yasson.customization.model.CreatorWithoutJavabeanProperty;
+import org.eclipse.yasson.customization.model.CreatorWithoutJsonbProperty1;
+import org.eclipse.yasson.customization.model.ParameterNameTester;
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbCreatorTest {
+
+    @Test
+    public void testRootConstructor() {
+        String json = "{\"str1\":\"abc\",\"str2\":\"def\",\"bigDec\":25}";
+        CreatorConstructorPojo pojo = defaultJsonb.fromJson(json, CreatorConstructorPojo.class);
+        assertEquals("abc", pojo.str1);
+        assertEquals("def", pojo.str2);
+        assertEquals(new BigDecimal("25"), pojo.bigDec);
+    }
+
+    @Test
+    public void testRootFactoryMethod() {
+        String json = "{\"par1\":\"abc\",\"par2\":\"def\",\"bigDec\":25}";
+        CreatorFactoryMethodPojo pojo = defaultJsonb.fromJson(json, CreatorFactoryMethodPojo.class);
+        assertEquals("abc", pojo.str1);
+        assertEquals("def", pojo.str2);
+        assertEquals(new BigDecimal("25"), pojo.bigDec);
+    }
+
+    @Test
+    public void testRootCreatorWithInnerCreator() {
+        String json = "{\"str1\":\"abc\",\"str2\":\"def\",\"bigDec\":25, \"innerFactoryCreator\":{\"par1\":\"inn1\",\"par2\":\"inn2\",\"bigDec\":11}}";
+        CreatorConstructorPojo pojo = defaultJsonb.fromJson(json, CreatorConstructorPojo.class);
+        assertEquals("abc", pojo.str1);
+        assertEquals("def", pojo.str2);
+        assertEquals(new BigDecimal("25"), pojo.bigDec);
+
+        assertEquals("inn1", pojo.innerFactoryCreator.str1);
+        assertEquals("inn2", pojo.innerFactoryCreator.str2);
+        assertEquals(new BigDecimal("11"), pojo.innerFactoryCreator.bigDec);
+    }
+
+    @Test
+    public void testIncompatibleFactoryMethodReturnType() {
+        try {
+        	defaultJsonb.fromJson("{\"s1\":\"abc\"}", CreatorIncompatibleTypePojo.class);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("Return type of creator"));
+        }
+    }
+
+    @Test
+    public void testMultipleCreatorsError() {
+        try {
+        	defaultJsonb.fromJson("{\"s1\":\"abc\"}", CreatorMultipleDeclarationErrorPojo.class);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("More than one @JsonbCreator"));
+        }
+    }
+
+    @Test
+    public void testCreatorWithoutJsonbParameters1() {
+        //arg2 is missing in json document
+        CreatorWithoutJsonbProperty1 object = defaultJsonb.fromJson("{\"arg0\":\"abc\", \"s2\":\"def\"}",
+                                                                    CreatorWithoutJsonbProperty1.class);
+        assertThat(object.getPar1(), is("abc"));
+        assertThat(object.getPar2(), is("def"));
+        assertThat(object.getPar3(), is((byte) 0));
+    }
+
+    @Test
+    public void testCreatorWithoutJavabeanProperty() {
+        final CreatorWithoutJavabeanProperty result = defaultJsonb.fromJson("{\"s1\":\"abc\", \"s2\":\"def\"}", CreatorWithoutJavabeanProperty.class);
+        assertEquals("abcdef", result.getStrField());
+
+    }
+
+    @Test
+    public void testPackagePrivateCreator() {
+    	assertThrows(JsonbException.class, () -> defaultJsonb.fromJson("{\"strVal\":\"abc\", \"intVal\":5}", CreatorPackagePrivateConstructor.class));
+    }
+
+    @Test
+    public void testLocalizedConstructor() {
+        String json = "{\"localDate\":\"05-09-2017\"}";
+        DateConstructor result = defaultJsonb.fromJson(json, DateConstructor.class);
+        assertEquals(LocalDate.of(2017, 9, 5), result.localDate);
+    }
+
+    @Test
+    public void testLocalizedConstructorMergedWithProperty() {
+        String json = "{\"localDate\":\"05-09-2017\"}";
+        DateConstructorMergedWithProperty result = defaultJsonb.fromJson(json, DateConstructorMergedWithProperty.class);
+        assertEquals(LocalDate.of(2017, 9, 5), result.localDate);
+    }
+
+    @Test
+    public void testLocalizedFactoryParameter() {
+        String json = "{\"number\":\"10.000\"}";
+        FactoryNumberParam result = defaultJsonb.fromJson(json, FactoryNumberParam.class);
+        assertEquals(BigDecimal.TEN, result.number);
+    }
+
+    @Test
+    public void testLocalizedFactoryParameterMergedWithProperty() {
+        String json = "{\"number\":\"10.000\"}";
+        FactoryNumberParamMergedWithProperty result = defaultJsonb.fromJson(json, FactoryNumberParamMergedWithProperty.class);
+        assertEquals(BigDecimal.TEN, result.number);
+    }
+
+    @Test
+    public void testCorrectCreatorParameterNames() {
+        String json = "{\"string\":\"someText\", \"someParam\":null }";
+        ParameterNameTester result = defaultJsonb.fromJson(json, ParameterNameTester.class);
+        assertEquals("someText", result.name);
+        assertNull(result.secondParam);
+    }
+
+    @Test
+    public void testGenericCreatorParameter() throws Exception {
+        final String json = "{\"persons\": [{\"name\": \"name1\"}]}";
+        Persons persons = defaultJsonb.fromJson(json, Persons.class);
+        assertEquals(1, persons.hiddenPersons.size());
+        assertEquals("name1", persons.hiddenPersons.iterator().next().getName());
+    }
+
+    public static final class Persons {
+
+        Set<Person> hiddenPersons;
+
+        private Persons(Set<Person> persons) {
+            this.hiddenPersons = persons;
+        }
+
+        @JsonbCreator
+        public static Persons wrap(@JsonbProperty("persons") Set<Person> persons) {
+            return new Persons(persons);
+        }
+
+        public Set<Person> getPersons() {
+            return null;
+        }
+    }
+
+    public static final class Person {
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+
+    public static final class DateConstructor {
+        public LocalDate localDate;
+
+        @JsonbCreator
+        public DateConstructor(@JsonbProperty("localDate") @JsonbDateFormat(value = "dd-MM-yyyy", locale = "nl-NL") LocalDate localDate) {
+            this.localDate = localDate;
+        }
+
+    }
+
+    public static final class DateConstructorMergedWithProperty {
+        @JsonbDateFormat(value = "dd-MM-yyyy", locale = "cs-CZ")
+        public LocalDate localDate;
+
+        @JsonbCreator
+        public DateConstructorMergedWithProperty(@JsonbProperty("localDate") LocalDate localDate) {
+            this.localDate = localDate;
+        }
+
+    }
+
+    public static final class FactoryNumberParam {
+        public BigDecimal number;
+
+        private FactoryNumberParam(BigDecimal number) {
+            this.number = number;
+        }
+
+        @JsonbCreator
+        public static FactoryNumberParam createInstance(
+                @JsonbProperty("number") @JsonbNumberFormat(value = "000.000", locale = "en-us")
+                        BigDecimal number) {
+            return new FactoryNumberParam(number);
+        }
+
+    }
+
+    public static final class FactoryNumberParamMergedWithProperty {
+
+        @JsonbNumberFormat(value = "000.000", locale = "en-us")
+        public BigDecimal number;
+
+        private FactoryNumberParamMergedWithProperty(BigDecimal number) {
+            this.number = number;
+        }
+
+        @JsonbCreator
+        public static FactoryNumberParamMergedWithProperty createInstance(@JsonbProperty("number") BigDecimal number) {
+            return new FactoryNumberParamMergedWithProperty(number);
+        }
+
+    }
+
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbDateFormatterTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbDateFormatterTest.java
new file mode 100644
index 0000000..3a10228
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/JsonbDateFormatterTest.java
@@ -0,0 +1,216 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.customization.model.DateFormatPojo;
+import org.eclipse.yasson.customization.model.DateFormatPojoWithClassLevelFormatter;
+import org.eclipse.yasson.customization.model.TrimmedDatePojo;
+import org.eclipse.yasson.internal.JsonBindingBuilder;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+import static org.eclipse.yasson.YassonConfig.ZERO_TIME_PARSE_DEFAULTING;
+
+/**
+ * Tests customization of date fields via {@link jakarta.json.bind.annotation.JsonbDateFormat} annotation
+ *
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class JsonbDateFormatterTest {
+
+    @Test
+    public void testCustomDateFormatSerialization() {
+        final Calendar timeCalendar = new Calendar.Builder()
+                .setDate(2017, Calendar.MARCH, 3)
+                .setTimeOfDay(11, 11, 10)
+                .setTimeZone(TimeZone.getTimeZone("UTC"))
+                .build();
+
+        DateFormatPojo dateFormatPojo = new DateFormatPojo();
+        dateFormatPojo.plainDateField = timeCalendar.getTime();
+        dateFormatPojo.formattedDateField = timeCalendar.getTime();
+        dateFormatPojo.getterFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.setterFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.getterAndFieldFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.setterAndFieldFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.getterAndSetterFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.getterAndSetterAndFieldFormattedDateField = timeCalendar.getTime();
+
+        String expectedJson = "{\"formattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndSetterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndSetterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"plainDateField\":\"2017-03-03T11:11:10Z[UTC]\",\"setterAndFieldFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"setterFormattedDateField\":\"2017-03-03T11:11:10Z[UTC]\"}";
+
+        assertEquals(expectedJson, defaultJsonb.toJson(dateFormatPojo));
+    }
+
+    @Test
+    public void testCustomDateFormatDeserialization() {
+        final Calendar timeCalendar = new Calendar.Builder()
+                .setDate(2017, Calendar.MARCH, 3)
+                .setTimeOfDay(11, 11, 10)
+                .setTimeZone(TimeZone.getTimeZone("UTC"))
+                .build();
+
+        DateFormatPojo result = defaultJsonb.fromJson("{\"formattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterAndSetterAndFieldFormattedDateField\":\"11:11:10 $$ 03-03-2017\",\"getterAndSetterFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndFieldFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterFormattedDateField\":\"2017-03-03T11:11:10\",\"plainDateField\":\"2017-03-03T11:11:10\",\"setterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"setterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\"}", DateFormatPojo.class);
+
+        assertEquals(timeCalendar.getTime(), result.plainDateField);
+        assertEquals(timeCalendar.getTime(), result.formattedDateField);
+        assertEquals(timeCalendar.getTime(), result.getterFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.setterFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.getterAndFieldFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.setterAndFieldFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.getterAndSetterFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.getterAndSetterAndFieldFormattedDateField);
+    }
+
+    @Test
+    public void testCustomDateFormatSerializationWithClassLevelDateFormatterDefined() {
+        final Calendar timeCalendar = new Calendar.Builder()
+                .setDate(2017, Calendar.MARCH, 3)
+                .setTimeOfDay(11, 11, 10)
+                .setTimeZone(TimeZone.getTimeZone("UTC"))
+                .build();
+
+        DateFormatPojoWithClassLevelFormatter dateFormatPojo = new DateFormatPojoWithClassLevelFormatter();
+        dateFormatPojo.plainDateField = timeCalendar.getTime();
+        dateFormatPojo.formattedDateField = timeCalendar.getTime();
+        dateFormatPojo.getterFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.setterFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.getterAndFieldFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.setterAndFieldFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.getterAndSetterFormattedDateField = timeCalendar.getTime();
+        dateFormatPojo.getterAndSetterAndFieldFormattedDateField = timeCalendar.getTime();
+
+        String expectedJson = "{\"formattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndSetterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndSetterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"plainDateField\":\"11:11:10 ^ 03-03-2017\",\"setterAndFieldFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"setterFormattedDateField\":\"11:11:10 ^ 03-03-2017\"}";
+
+        assertEquals(expectedJson, defaultJsonb.toJson(dateFormatPojo));
+    }
+
+    @Test
+    public void testCustomDateFormatDeserializationWithClassLevelDateFormatterDefined() {
+        final Calendar timeCalendar = new Calendar.Builder()
+                .setDate(2017, Calendar.MARCH, 3)
+                .setTimeOfDay(11, 11, 10)
+                .setTimeZone(TimeZone.getTimeZone("UTC"))
+                .build();
+
+        DateFormatPojoWithClassLevelFormatter result = defaultJsonb.fromJson("{\"formattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterAndSetterAndFieldFormattedDateField\":\"11:11:10 $$ 03-03-2017\",\"getterAndSetterFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"getterAndFieldFormattedDateField\":\"11:11:10 ^^ 03-03-2017\",\"getterFormattedDateField\":\"11:11:10 ^ 03-03-2017\",\"plainDateField\":\"11:11:10 ^ 03-03-2017\",\"setterAndFieldFormattedDateField\":\"11:11:10 <> 03-03-2017\",\"setterFormattedDateField\":\"11:11:10 ^^ 03-03-2017\"}", DateFormatPojoWithClassLevelFormatter.class);
+
+        assertEquals(timeCalendar.getTime(), result.plainDateField);
+        assertEquals(timeCalendar.getTime(), result.formattedDateField);
+        assertEquals(timeCalendar.getTime(), result.getterFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.setterFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.getterAndFieldFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.setterAndFieldFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.getterAndSetterFormattedDateField);
+        assertEquals(timeCalendar.getTime(), result.getterAndSetterAndFieldFormattedDateField);
+    }
+
+    @Test
+    public void testTrimmedDateParsing() {
+        ZoneId utcZone = ZoneId.of("UTC");
+        ZonedDateTime zdt = ZonedDateTime.of(2018, 1, 30, 0, 0, 0, 0, utcZone);
+
+        TrimmedDatePojo pojo = new TrimmedDatePojo();
+        pojo.setDate(Date.from(zdt.toInstant()));
+        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(utcZone));
+        calendar.setTime(pojo.getDate());
+        pojo.setCalendar(calendar);
+        pojo.setLocalDateTime(zdt.toLocalDateTime());
+        pojo.setZonedDateTime(zdt);
+        pojo.setZonedDateTimeNanosOfDay(zdt.plusNanos(3000));
+        pojo.setZonedDateTimeHoursAndSeconds(zdt);
+        pojo.setZonedDateTimeOverriddenZone(zdt.withZoneSameInstant(ZoneId.of("Europe/Paris")));
+        pojo.setZonedInstant(zdt.withZoneSameInstant(ZoneId.of("Europe/Paris")).toInstant());
+
+        Jsonb zeroDefaultingJsonb = new JsonBindingBuilder()
+                .withConfig(new JsonbConfig().setProperty(ZERO_TIME_PARSE_DEFAULTING, true))
+                .build();
+
+        String serialized = zeroDefaultingJsonb.toJson(pojo);
+        assertEquals(
+                "{\"calendar\":\"2018.01.30\",\"date\":\"2018.01.30\",\"localDateTime\":\"2018.01.30\",\"zonedDateTime\":\"2018.01.30\",\"zonedDateTimeHoursAndSeconds\":\"2018.01.30 00:00\",\"zonedDateTimeNanosOfDay\":\"2018.01.30 3000\",\"zonedDateTimeOverriddenZone\":\"2018.01.30 Europe/Paris\",\"zonedInstant\":\"2018.01.30 UTC\"}",
+                serialized);
+
+        String jsonToDeserialize = "{\"calendar\":\"2018.01.30\",\"date\":\"2018.01.30\",\"localDateTime\":\"2018.01.30\",\"zonedDateTime\":\"2018.01.30\",\"zonedDateTimeHoursAndSeconds\":\"2018.01.30 12:15\",\"zonedDateTimeNanosOfDay\":\"2018.01.30 9000\",\"zonedDateTimeOverriddenZone\":\"2018.01.30 Europe/Prague\",\"zonedInstant\":\"2018.01.30 Europe/Prague\"}";
+        TrimmedDatePojo trimmedDatePojo = zeroDefaultingJsonb.fromJson(jsonToDeserialize, TrimmedDatePojo.class);
+
+        //Nanos are overridden in json for deserialization. Tests that defaulting hour/minute/second does not affect other units.
+        assertEquals(2018, trimmedDatePojo.getZonedDateTimeNanosOfDay().getYear());
+        assertEquals(1, trimmedDatePojo.getZonedDateTimeNanosOfDay().getMonthValue());
+        assertEquals(30, trimmedDatePojo.getZonedDateTimeNanosOfDay().getDayOfMonth());
+        assertEquals(0, trimmedDatePojo.getZonedDateTimeNanosOfDay().getHour());
+        assertEquals(0, trimmedDatePojo.getZonedDateTimeNanosOfDay().getMinute());
+        assertEquals(0, trimmedDatePojo.getZonedDateTimeNanosOfDay().getSecond());
+        assertEquals(9000, trimmedDatePojo.getZonedDateTimeNanosOfDay().getNano());
+        assertEquals(utcZone, trimmedDatePojo.getZonedDateTimeNanosOfDay().getZone());
+
+        //Test trimmed zoned date time have correct values.
+        assertEquals(2018, trimmedDatePojo.getZonedDateTime().getYear());
+        assertEquals(1, trimmedDatePojo.getZonedDateTime().getMonthValue());
+        assertEquals(30, trimmedDatePojo.getZonedDateTime().getDayOfMonth());
+        assertEquals(0, trimmedDatePojo.getZonedDateTime().getHour());
+        assertEquals(0, trimmedDatePojo.getZonedDateTime().getMinute());
+        assertEquals(0, trimmedDatePojo.getZonedDateTime().getSecond());
+        assertEquals(utcZone, trimmedDatePojo.getZonedDateTime().getZone());
+
+
+        //Zone is overridden in JSON, causing
+        assertEquals(2018, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getYear());
+        assertEquals(1, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getMonthValue());
+        assertEquals(30, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getDayOfMonth());
+        assertEquals(12, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getHour());
+        assertEquals(0, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getMinute());
+        assertEquals(15, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getSecond());
+        assertEquals(utcZone, trimmedDatePojo.getZonedDateTimeHoursAndSeconds().getZone());
+
+        //Defaulting UTC zone is overridden in JSON.
+        assertEquals(2018, trimmedDatePojo.getZonedDateTimeOverriddenZone().getYear());
+        assertEquals(1, trimmedDatePojo.getZonedDateTimeOverriddenZone().getMonthValue());
+        assertEquals(30, trimmedDatePojo.getZonedDateTimeOverriddenZone().getDayOfMonth());
+        assertEquals(0, trimmedDatePojo.getZonedDateTimeOverriddenZone().getHour());
+        assertEquals(0, trimmedDatePojo.getZonedDateTimeOverriddenZone().getMinute());
+        assertEquals(0, trimmedDatePojo.getZonedDateTimeOverriddenZone().getSecond());
+        assertEquals(ZoneId.of("Europe/Prague"), trimmedDatePojo.getZonedDateTimeOverriddenZone().getZone());
+
+        //Tests LocalDateTime trimmed values
+        assertEquals(2018, trimmedDatePojo.getLocalDateTime().getYear());
+        assertEquals(1, trimmedDatePojo.getLocalDateTime().getMonthValue());
+        assertEquals(30, trimmedDatePojo.getLocalDateTime().getDayOfMonth());
+        assertEquals(0, trimmedDatePojo.getLocalDateTime().getHour());
+        assertEquals(0, trimmedDatePojo.getLocalDateTime().getMinute());
+        assertEquals(0, trimmedDatePojo.getLocalDateTime().getSecond());
+
+        //Test date and instant have correct time.
+        assertEquals(zdt.toInstant().toEpochMilli(), trimmedDatePojo.getDate().getTime());
+        assertEquals(zdt.withZoneSameLocal(ZoneId.of("Europe/Prague")).toInstant().toEpochMilli(), trimmedDatePojo.getZonedInstant().toEpochMilli());
+
+        //Test calendar instance
+        //Tests LocalDateTime trimmed values
+        assertEquals(2018, trimmedDatePojo.getCalendar().get(Calendar.YEAR));
+        assertEquals(0, trimmedDatePojo.getCalendar().get(Calendar.MONTH));
+        assertEquals(30, trimmedDatePojo.getCalendar().get(Calendar.DAY_OF_MONTH));
+        assertEquals(0, trimmedDatePojo.getCalendar().get(Calendar.HOUR_OF_DAY));
+        assertEquals(0, trimmedDatePojo.getCalendar().get(Calendar.MINUTE));
+        assertEquals(0, trimmedDatePojo.getCalendar().get(Calendar.SECOND));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbNillableTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbNillableTest.java
new file mode 100644
index 0000000..1d5d40b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/JsonbNillableTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.customization.model.JsonbNillableClassSecondLevel;
+import org.eclipse.yasson.customization.model.JsonbNillableOverriddenWithJsonbProperty;
+import org.eclipse.yasson.customization.model.JsonbNillableOverridesClass;
+import org.eclipse.yasson.customization.model.JsonbNillableOverridesInterface;
+import org.eclipse.yasson.customization.model.JsonbNillableValue;
+import org.eclipse.yasson.customization.model.packagelevelannotations.JsonbNillablePackageLevel;
+import org.eclipse.yasson.customization.model.packagelevelannotations.PackageLevelOverriddenWithClassLevel;
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+
+/**
+ * Tests a {@link jakarta.json.bind.annotation.JsonbNillable} annotation.
+ * @author Roman Grigoriadi
+ */
+public class JsonbNillableTest {
+
+    @Test
+    public void testJsonbNillable() {
+        JsonbNillableValue pojo = new JsonbNillableValue();
+        assertEquals("{\"nillableField\":null}", defaultJsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testJsonbNillableOverriddenWithJsonbProperty() {
+        JsonbNillableOverriddenWithJsonbProperty pojo = new JsonbNillableOverriddenWithJsonbProperty();
+        assertEquals("{}", defaultJsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testPackageLevelNillable() {
+        JsonbNillablePackageLevel pojo = new JsonbNillablePackageLevel();
+        assertEquals("{\"packageLevelNillableField\":null}", defaultJsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testPackageLevelOverriddenWithClassLevel() {
+        PackageLevelOverriddenWithClassLevel pojo = new PackageLevelOverriddenWithClassLevel();
+        assertEquals("{}", defaultJsonb.toJson(pojo));
+    }
+
+    /**
+     * Tests inheritance of annotations from interfaces.
+     */
+    @Test
+    public void testNillableInheritFromInterface() throws Exception {
+        JsonbNillableClassSecondLevel pojo = new JsonbNillableClassSecondLevel();
+        assertEquals("{\"classNillable\":null}", defaultJsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testInheritanceOverride() throws Exception {
+        JsonbNillableOverridesInterface overridesInterface = new JsonbNillableOverridesInterface();
+        assertEquals("{}", defaultJsonb.toJson(overridesInterface));
+
+        JsonbNillableOverridesClass overridesClass = new JsonbNillableOverridesClass();
+        assertEquals("{}", defaultJsonb.toJson(overridesClass));
+    }
+
+    @Test
+    public void testNillableInConfig() {
+        String jsonString = nullableJsonb.toJson(new ScalarValueWrapper<String>(){});
+        assertEquals("{\"value\":null}", jsonString);
+    }
+    
+    public static class PrimitiveNullBoolean {
+
+        @JsonbProperty(nillable = true)
+        private Boolean someBoolean;
+
+        void setSomeBoolean(boolean value) { // note that value is a primitive boolean
+            // leaving this empty, exception will be thrown before Yasson gets here.
+        }
+    }
+    
+    /**
+     * Test for issue https://github.com/eclipse-ee4j/yasson/issues/399
+     */
+    @Test
+    public void testNillableSomeBoolean() {
+    	Jsonb jsonb = JsonbBuilder.create();
+        String input = "{\"someBoolean\": null}";
+        PrimitiveNullBoolean deserialized = jsonb.fromJson(input, PrimitiveNullBoolean.class);
+        assertNull(deserialized.someBoolean);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java
new file mode 100644
index 0000000..5e6c52a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyTest.java
@@ -0,0 +1,265 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.customization.model.JsonbPropertyName;
+import org.eclipse.yasson.customization.model.JsonbPropertyNameCollision;
+import org.eclipse.yasson.customization.model.JsonbPropertyNillable;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+
+/**
+ * Tests parsing of {@link jakarta.json.bind.annotation.JsonbProperty} test.
+ * @author Roman Grigoriadi
+ */
+public class JsonbPropertyTest {
+
+    @Test
+    public void testPropertyName() throws Exception {
+
+        JsonbPropertyName pojo = new JsonbPropertyName();
+        pojo.setFieldAnnotatedName("FIELD_ANNOTATED");
+        pojo.setMethodAnnotName("METHOD_ANNOTATED");
+        pojo.setFieldOverriddenWithMethodAnnot("OVERRIDDEN_GETTER");
+
+        assertEquals("{\"fieldAnnotatedNameCustomized\":\"FIELD_ANNOTATED\",\"getterAnnotatedName\":\"METHOD_ANNOTATED\",\"getterOverriddenName\":\"OVERRIDDEN_GETTER\"}",
+                defaultJsonb.toJson(pojo));
+
+        String toUnmarshall = "{\"fieldAnnotatedNameCustomized\":\"FIELD_ANNOTATED\",\"setterOverriddenName\":\"OVERRIDDEN_GETTER\",\"setterAnnotatedName\":\"METHOD_ANNOTATED\"}";
+        JsonbPropertyName result = defaultJsonb.fromJson(toUnmarshall, JsonbPropertyName.class);
+        assertEquals("FIELD_ANNOTATED", result.getFieldAnnotatedName());
+        assertEquals("METHOD_ANNOTATED", result.getMethodAnnotName());
+        assertEquals("OVERRIDDEN_GETTER", result.getFieldOverriddenWithMethodAnnot());
+    }
+
+    @Test
+    public void testNameCollision() {
+        JsonbPropertyNameCollision nameCollisionPojo = new JsonbPropertyNameCollision();
+        tryClash(() -> defaultJsonb.toJson(nameCollisionPojo));
+        tryClash(() -> defaultJsonb.fromJson("{}", JsonbPropertyNameCollision.class));
+    }
+
+
+
+    private static void tryClash(Runnable clashCommand) {
+        try {
+            clashCommand.run();
+            fail();
+        } catch (Exception e) {
+            assertTrue(e.getMessage().startsWith("Property pojoName clashes with property pojoNameCollision"));
+        }
+    }
+
+
+    @Test
+    public void testPropertyNillable() {
+        JsonbPropertyNillable pojo = new JsonbPropertyNillable();
+        assertEquals("{\"nullField\":null}", defaultJsonb.toJson(pojo));
+    }
+    
+    @Test
+    public void testRenamedGetterAndSetter() {
+        // Reported in issue: https://github.com/eclipse-ee4j/yasson/issues/355
+        final RenamedGetterAndSetter b = new RenamedGetterAndSetter();
+        b.setTest("hi");
+        final String h = defaultJsonb.toJson(b);
+        final String expectedJson = "{\"apple\":\"hi\"}";
+        assertEquals(expectedJson, h); //this passes
+        final RenamedGetterAndSetter b1 = defaultJsonb.fromJson(h, RenamedGetterAndSetter.class);
+        
+        assertEquals("hi", b1.getTest()); //this fails but passes in 1.0.4
+    }
+    
+    @Test
+    public void testRenamedGetterAndSetter2() {
+        // Reported in issue: https://github.com/eclipse-ee4j/yasson/issues/81
+        final Jsonb jsonb = JsonbBuilder.create(
+                new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE));
+
+        final RenamedGetterAndSetter2 bean1 = new RenamedGetterAndSetter2();
+        bean1.setAPIDocumentation("REST");
+
+        final String json = jsonb.toJson(bean1);
+        final RenamedGetterAndSetter2 bean2 = jsonb.fromJson(json, RenamedGetterAndSetter2.class);
+        assertEquals(bean1.getAPIDocumentation(), bean2.getAPIDocumentation());
+    }
+    
+    @Test
+    public void testRenamedGetterAndSetter3() {
+        // Reported in issue: https://github.com/eclipse-ee4j/yasson/issues/81
+        final Jsonb jsonb = JsonbBuilder.create();
+        
+        final RenamedGetterAndSetter2 bean1 = new RenamedGetterAndSetter2();
+        bean1.setAPIDocumentation("REST");
+
+        final String json = jsonb.toJson(bean1);
+        final RenamedGetterAndSetter2 bean2 = jsonb.fromJson(json, RenamedGetterAndSetter2.class);
+        assertEquals(bean1.getAPIDocumentation(), bean2.getAPIDocumentation());
+    }
+    
+    public static class RenamedGetterAndSetter {
+        private String apple;
+        
+        @JsonbProperty("apple")
+        public String getTest() {
+            return apple;
+        }
+
+        @JsonbProperty("apple")
+        public void setTest(String test) {
+            this.apple = test;
+        }
+    }
+    
+    public static class RenamedGetterAndSetter2 {
+        
+        private String api;
+        
+        @JsonbProperty("api")
+        public String getAPIDocumentation() {
+            return api;
+        }
+        
+        @JsonbProperty("api")
+        public void setAPIDocumentation(String api) {
+            this.api = api;
+        }
+    }
+
+    /**
+     * In this test getter / setter doesn't match to field "doi", because declared by javabean convention.
+     * When model is parsed there are to properties:
+     * Property for private field "doi", without customization. This property is not readable because field
+     * is private without getter / setter.
+     * And property "DOI" - getter / setter without a field with customization on getter renaming it to doi
+     * in serialized document.
+     *
+     * Because first of those properties is not readable this should not raise naming clash error.
+     */
+    @Test
+    public void testNonConflictingProperties() {
+        NonConflictingProperties nonConflictingProperties = new NonConflictingProperties();
+        nonConflictingProperties.setDOI("DOI value");
+
+        String json = defaultJsonb.toJson(nonConflictingProperties);
+        assertEquals("{\"doi\":\"DOI value\"}", json);
+
+        NonConflictingProperties result = defaultJsonb.fromJson("{\"DOI\":\"DOI value\"}", NonConflictingProperties.class);
+        assertEquals("DOI value", result.getDOI());
+    }
+
+    /**
+     * Same problem as above but now field is public, so clash takes place.
+     */
+    @Test
+    public void testConflictingProperties() {
+        ConflictingProperties conflictingProperties = new ConflictingProperties();
+        conflictingProperties.setDOI("DOI value");
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+
+        try {
+            jsonb.toJson(conflictingProperties);
+            fail();
+        } catch (JsonbException e) {
+            if (!e.getMessage().equals("Property DOI clashes with property doi by read or write name in class org.eclipse.yasson.customization.JsonbPropertyTest$ConflictingProperties.")) {
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * Tests clash with property altered by naming strategy.
+     */
+    @Test
+    public void testConflictingWithUpperCamelStrategy() {
+        ConflictingWithUpperCamelStrategy pojo = new ConflictingWithUpperCamelStrategy();
+        pojo.setDOI("DOI value");
+
+        String json = defaultJsonb.toJson(pojo);
+        assertEquals("{\"Doi\":\"DOI value\",\"doi\":\"DOI value\"}", json);
+
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
+                .withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE));
+
+        try {
+            jsonb.toJson(pojo);
+            fail();
+        } catch (JsonbException e) {
+            if (!e.getMessage().equals("Property DOI clashes with property doi by read or write name in class org.eclipse.yasson.customization.JsonbPropertyTest$ConflictingWithUpperCamelStrategy.")) {
+                throw e;
+            }
+        }
+
+    }
+    
+    @Test
+    public void testConflictingWithLowercaseStrategy() {
+    	// scenario raised by user here: https://github.com/eclipse-ee4j/yasson/issues/296
+    	Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES));
+    	assertEquals("{\"url\":\"http://foo.com\"}", 
+    			jsonb.toJson(new ConflictingIfLowercase()));
+    }
+    
+    public static class ConflictingIfLowercase {
+    	private String url = "foo.com";
+    	
+    	public String getURL() {
+    		return "http://" + url;
+    	}
+    }
+
+    public static class NonConflictingProperties {
+        private String doi;
+
+        @JsonbProperty("doi")
+        public String getDOI() {
+            return doi;
+        }
+        public void setDOI(String doi) {
+            this.doi = doi;
+        }
+    }
+
+    public static class ConflictingProperties {
+        public String doi;
+
+        @JsonbProperty("doi")
+        public String getDOI() {
+            return doi;
+        }
+        public void setDOI(String doi) {
+            this.doi = doi;
+        }
+    }
+
+    public static class ConflictingWithUpperCamelStrategy {
+        public String doi;
+
+        @JsonbProperty("Doi")
+        public String getDOI() {
+            return doi;
+        }
+        public void setDOI(String doi) {
+            this.doi = doi;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/JsonbPropertyVisibilityStrategyTest.java b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyVisibilityStrategyTest.java
new file mode 100644
index 0000000..551e059
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/JsonbPropertyVisibilityStrategyTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbVisibility;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * Tests custom {@link PropertyVisibilityStrategy}
+ *
+ * @author Roman Grigoriadi
+ */
+public class JsonbPropertyVisibilityStrategyTest {
+
+    public static class FieldPojo {
+
+        private String afield;
+        private String bfield;
+        public String cfield;
+        public String dfield;
+
+        public FieldPojo(String afield, String bfield, String cfield, String dfield) {
+            this.afield = afield;
+            this.bfield = bfield;
+            this.cfield = cfield;
+            this.dfield = dfield;
+        }
+    }
+
+    public static class GetterPojo {
+        public String getAgetter() {
+            return "avalue";
+        }
+        public String getBgetter() {
+            return "bvalue";
+        }
+        private String getCgetter() {
+            return "cvalue";
+        }
+        private String getDgetter() {
+            return "dvalue";
+        }
+    }
+
+    @JsonbVisibility(TestVisibilityStrategy.class)
+    public static final class AnnotatedPojo {
+
+        private String afield;
+        private String bfield;
+        public String cfield;
+        public String dfield;
+
+        public AnnotatedPojo(String afield, String bfield, String cfield, String dfield) {
+            this.afield = afield;
+            this.bfield = bfield;
+            this.cfield = cfield;
+            this.dfield = dfield;
+        }
+
+        public String getAgetter() {
+            return "avalue";
+        }
+        public String getBgetter() {
+            return "bvalue";
+        }
+        private String getCgetter() {
+            return "cvalue";
+        }
+        private String getDgetter() {
+            return "dvalue";
+        }
+    }
+
+
+    public static final class TestVisibilityStrategy implements PropertyVisibilityStrategy {
+        @Override
+        public boolean isVisible(Field field) {
+            final String fieldName = field.getName();
+            return fieldName.equals("bfield") || fieldName.equals("cfield");
+        }
+
+        @Override
+        public boolean isVisible(Method method) {
+            final String methodName = method.getName();
+            return methodName.equals("getBgetter") || methodName.equals("getCgetter");
+        }
+    }
+
+    /**
+     * Tests applying for both public and nonpublic fields.
+     */
+    @Test
+    public void testFieldVisibilityStrategy() {
+        JsonbConfig customizedConfig = new JsonbConfig();
+        customizedConfig.setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() {
+            @Override
+            public boolean isVisible(Field field) {
+                final String fieldName = field.getName();
+                return fieldName.equals("afield") || fieldName.equals("dfield");
+            }
+
+            @Override
+            public boolean isVisible(Method method) {
+                throw new IllegalStateException("Not supported");
+            }
+        });
+
+        FieldPojo fieldPojo = new FieldPojo("avalue", "bvalue", "cvalue", "dvalue");
+
+        Jsonb jsonb = JsonbBuilder.create(customizedConfig);
+        assertEquals("{\"afield\":\"avalue\",\"dfield\":\"dvalue\"}", jsonb.toJson(fieldPojo));
+    }
+
+    /**
+     * Tests applying for both public and nonpublic getters.
+     */
+    @Test
+    public void testMethodVisibilityStrategy() {
+        JsonbConfig customizedConfig = new JsonbConfig();
+        customizedConfig.setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() {
+            @Override
+            public boolean isVisible(Field field) {
+                throw new IllegalStateException("Not supported");
+            }
+
+            @Override
+            public boolean isVisible(Method method) {
+                final String methodName = method.getName();
+                return methodName.equals("getAgetter") || methodName.equals("getDgetter");
+            }
+        });
+
+        GetterPojo getterPojo = new GetterPojo();
+
+        Jsonb jsonb = JsonbBuilder.create(customizedConfig);
+        assertEquals("{\"agetter\":\"avalue\",\"dgetter\":\"dvalue\"}", jsonb.toJson(getterPojo));
+    }
+
+    @Test
+    public void testAnnotatedPojo() {
+        AnnotatedPojo fieldPojo = new AnnotatedPojo("avalue", "bvalue", "cvalue", "dvalue");
+        assertEquals("{\"bfield\":\"bvalue\",\"bgetter\":\"bvalue\",\"cfield\":\"cvalue\",\"cgetter\":\"cvalue\"}", defaultJsonb.toJson(fieldPojo));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/NumberFormatTest.java b/src/test/java/org/eclipse/yasson/customization/NumberFormatTest.java
new file mode 100644
index 0000000..54d87c7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/NumberFormatTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.customization.model.NumberFormatPojo;
+import org.eclipse.yasson.customization.model.NumberFormatPojoWithoutClassLevelFormatter;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Locale;
+import jakarta.json.bind.JsonbConfig;
+
+/**
+ * Tests number format.
+ * @author Roman Grigoriadi
+ */
+public class NumberFormatTest {
+    private static final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withLocale(Locale.US));
+
+    @Test
+    public void testSerialize() {
+        NumberFormatPojo pojo = new NumberFormatPojo();
+        pojo.bigDecimal = BigDecimal.TEN;
+        pojo.bigInteger = BigInteger.ONE;
+        pojo.aDouble = .1d;
+        pojo.aFloat = .35f;
+        pojo.aLong = Long.MAX_VALUE;
+        pojo.integer = Integer.MAX_VALUE;
+        pojo.aShort = 1;
+        pojo.aByte = 127;
+        pojo.setDoubleGetterFormatted(.1d);
+        pojo.setDoubleSetterFormatted(.5d);
+        pojo.setDoubleSetterAndPropertyFormatter(0.6d);
+
+        String expectedJson = "{\"aByte\":\"127\",\"aDouble\":\"000.10000000\",\"aFloat\":\"000.34999999\",\"aLong\":\"9223372036854775807\",\"aShort\":\"00001\",\"bigDecimal\":\"00000010.000000\",\"bigInteger\":\"00000001\",\"doubleGetterFormatted\":\"000.10000000\",\"doubleSetterAndPropertyFormatter\":\"000.600\",\"doubleSetterFormatted\":\"0.5\",\"integer\":\"2147483647.0\"}";
+
+        assertEquals(expectedJson, jsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testDeserializer() {
+        String expectedJson = "{\"aByte\":\"127\",\"aDouble\":\"000.10000000\",\"aFloat\":\"000.34999999\",\"aLong\":\"9223372036854775807\",\"aShort\":\"00001\",\"bigDecimal\":\"00000010.000000\",\"bigInteger\":\"00000001\",\"doubleGetterFormatted\":\"000.10000\",\"doubleSetterFormatted\":\",005\",\"doubleSetterAndPropertyFormatter\":\"000,600\",\"integer\":\"2147483647.0\"}";
+        NumberFormatPojo pojo = jsonb.fromJson(expectedJson, NumberFormatPojo.class);
+
+        assertEquals(BigDecimal.TEN, pojo.bigDecimal);
+        assertEquals(BigInteger.ONE, pojo.bigInteger);
+        assertEquals(Byte.valueOf((byte) 127), pojo.aByte);
+        assertEquals(Double.valueOf(.1d), pojo.aDouble);
+        assertEquals(Float.valueOf(.35f), pojo.aFloat);
+        assertEquals((Integer)Integer.MAX_VALUE, pojo.integer);
+        assertEquals(Short.valueOf((short) 1), pojo.aShort);
+        assertEquals((Long)Long.MAX_VALUE, pojo.aLong);
+        assertEquals(Double.valueOf(.1d), pojo.getDoubleGetterFormatted());
+        assertEquals(Double.valueOf(.005d), pojo.getDoubleSetterFormatted());
+        assertEquals(Double.valueOf(.6d), pojo.getDoubleSetterAndPropertyFormatter());
+    }
+
+    @Test
+    public void testSerializeWithoutClassLevelFormatter() {
+        NumberFormatPojoWithoutClassLevelFormatter pojo = new NumberFormatPojoWithoutClassLevelFormatter();
+        pojo.setDoubleGetterFormatted(.1d);
+        pojo.setDoubleSetterFormatted(.5d);
+        pojo.setDoubleSetterAndPropertyFormatter(0.6d);
+
+        String expectedJson = "{\"doubleGetterFormatted\":\"000.10000000\",\"doubleSetterAndPropertyFormatter\":\"000.600\",\"doubleSetterFormatted\":0.5}";
+
+        assertEquals(expectedJson, jsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testDeserializeWithoutClassLevelFormatter() {
+        String expectedJson = "{\"doubleGetterFormatted\":\"000.10000\",\"doubleSetterFormatted\":\",005\",\"doubleSetterAndPropertyFormatter\":\"000,600\"}";
+        NumberFormatPojoWithoutClassLevelFormatter pojo = jsonb.fromJson(expectedJson, NumberFormatPojoWithoutClassLevelFormatter.class);
+
+        assertEquals(Double.valueOf(.1d), pojo.getDoubleGetterFormatted());
+        assertEquals(Double.valueOf(.005d), pojo.getDoubleSetterFormatted());
+        assertEquals(Double.valueOf(.6d), pojo.getDoubleSetterAndPropertyFormatter());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/PrettyPrintTest.java b/src/test/java/org/eclipse/yasson/customization/PrettyPrintTest.java
new file mode 100644
index 0000000..ada9c5f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/PrettyPrintTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import java.util.Arrays;
+
+/**
+ * Tests pretty print to JSONP propagation
+ *
+ * @author Roman Grigoriadi
+ */
+public class PrettyPrintTest {
+
+    @Test
+    public void testPrettyPrint() {
+        assertEquals("[\n    \"first\",\n    \"second\"\n]", formattingJsonb.toJson(Arrays.asList("first", "second")));
+    }
+
+    @Test
+    public void testPrettyPrintFalse() {
+        assertEquals("[\"first\",\"second\"]", defaultJsonb.toJson(Arrays.asList("first", "second")));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/PropertyOrderTest.java b/src/test/java/org/eclipse/yasson/customization/PropertyOrderTest.java
new file mode 100644
index 0000000..eee1d7d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/PropertyOrderTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.customization.model.FieldCustomOrder;
+import org.eclipse.yasson.customization.model.FieldCustomOrderWrapper;
+import org.eclipse.yasson.customization.model.FieldOrder;
+import org.eclipse.yasson.customization.model.FieldOrderNameAnnotation;
+import org.eclipse.yasson.customization.model.FieldSpecificOrder;
+import org.eclipse.yasson.customization.model.RenamedPropertiesContainer;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbPropertyOrder;
+import jakarta.json.bind.config.PropertyOrderStrategy;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class PropertyOrderTest {
+
+    @Test
+    public void testPropertySorting() {
+        FieldOrder fieldOrder = new FieldOrder();
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL));
+        String expectedLexicographical = "{\"aField\":\"aValue\",\"bField\":\"bValue\",\"cField\":\"cValue\",\"dField\":\"dValue\"}";
+        assertEquals(expectedLexicographical, jsonb.toJson(fieldOrder));
+
+        jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE));
+        String expectedReverse = "{\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\",\"aField\":\"aValue\"}";
+        assertEquals(expectedReverse, jsonb.toJson(fieldOrder));
+    }
+
+    @Test
+    public void testPropertyCustomOrder() {
+        FieldCustomOrder fieldCustomOrder = new FieldCustomOrder();
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL));
+        String expectedCustomOrder = "{\"aField\":\"aValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"bField\":\"bValue\"}";
+        assertEquals(expectedCustomOrder, jsonb.toJson(fieldCustomOrder));
+
+        FieldCustomOrderWrapper fieldCustomOrderWrapper = new FieldCustomOrderWrapper();
+        String expectedOrder = "{\"fieldCustomOrder\":{\"aField\":\"aValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"bField\":\"bValue\"},\"intField\":1,\"stringField\":\"stringValue\"}";
+        assertEquals(expectedOrder, jsonb.toJson(fieldCustomOrderWrapper));
+    }
+
+    @Test
+    public void testPropertySetCustomOrder() {
+        FieldSpecificOrder fieldSpecificOrder = new FieldSpecificOrder();
+        String expectedSpecific = "{\"aField\":\"aValue\",\"dField\":\"dValue\",\"bField\":\"bValue\",\"cField\":\"cValue\"}";
+        assertEquals(expectedSpecific, defaultJsonb.toJson(fieldSpecificOrder));
+        
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE));
+        expectedSpecific = "{\"aField\":\"aValue\",\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\"}";
+        assertEquals(expectedSpecific, jsonb.toJson(fieldSpecificOrder));
+    }
+
+    @Test
+    public void testPropertySortingWithNamingAnnotation() {
+        FieldOrderNameAnnotation fieldOrderNameAnnotation = new FieldOrderNameAnnotation();
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL));
+        String expectedLexicographical = "{\"bField\":\"bValue\",\"cField\":\"cValue\",\"dField\":\"dValue\",\"zField\":\"aValue\"}";
+        assertEquals(expectedLexicographical, jsonb.toJson(fieldOrderNameAnnotation));
+
+        jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE));
+        String expectedReverse = "{\"zField\":\"aValue\",\"dField\":\"dValue\",\"cField\":\"cValue\",\"bField\":\"bValue\"}";
+        assertEquals(expectedReverse, jsonb.toJson(fieldOrderNameAnnotation));
+    }
+
+    @Test
+    public void testLexicographicalPropertyOrderRenamedProperties() {
+        JsonbConfig config = new JsonbConfig();
+        config.setProperty(JsonbConfig.PROPERTY_ORDER_STRATEGY, PropertyOrderStrategy.LEXICOGRAPHICAL);
+        Jsonb jsonb = JsonbBuilder.create(config);
+
+        String jsonString = jsonb.toJson(new RenamedPropertiesContainer() {{ setStringInstance("Test String"); setLongInstance(1); }});
+        assertTrue(jsonString.matches("\\{\\s*\"first\"\\s*\\:\\s*0\\s*,\\s*\"second\"\\s*\\:\\s*\"Test String\"\\s*,\\s*\"third\"\\s*\\:\\s*1\\s*\\}"));
+
+        RenamedPropertiesContainer unmarshalledObject = jsonb.fromJson("{ \"first\" : 1, \"second\" : \"Test String\", \"third\" : 1 }", RenamedPropertiesContainer.class);
+        assertEquals(3, unmarshalledObject.getIntInstance());
+    }
+
+    @Test
+    public void testJsonbPropertyOrderOnRenamedProperties() {
+        assertEquals("{\"c\":11,\"d\":10,\"aExtra\":\"extra\"}", defaultJsonb.toJson(new Range(10, 11)));
+    }
+    
+    // By default, this object would use A-X ordering with property order:
+    // anExtraProp, propA, propB
+    // But with JsonbPropertyOrder we will put props 'propB' and 'propA' first, and leftovers will go at the end, resulting in:
+    // propB, propA, anExtraProp
+    @JsonbPropertyOrder({"propB","propA"})
+    public class Range {
+
+        @JsonbProperty("d")
+        public final int propA;
+
+        @JsonbProperty("c")
+        public final int propB;
+        
+        @JsonbProperty("aExtra")
+        public final String anExtraProp = "extra";
+
+        @JsonbCreator
+        public Range(
+                @JsonbProperty("d") int propA,
+                @JsonbProperty("c") int propB
+        ) {
+            this.propA = propA;
+            this. propB = propB;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/YassonSpecificConfigTests.java b/src/test/java/org/eclipse/yasson/customization/YassonSpecificConfigTests.java
new file mode 100644
index 0000000..4364ebb
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/YassonSpecificConfigTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization;
+
+import java.util.Optional;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import org.eclipse.yasson.YassonConfig;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests for Yasson specific config properties.
+ */
+public class YassonSpecificConfigTests {
+
+    private static final String NULL_VALUE_STRING = "null value handled";
+    private static final String NULL_VALUE_SERIALIZED = "\"" + NULL_VALUE_STRING + "\"";
+
+    @Test
+    public void nullRootSerializerTest() {
+        Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer()));
+        assertEquals(NULL_VALUE_SERIALIZED, jsonb.toJson(null));
+    }
+
+    @Test
+    public void emptyOptionalRootSerializerTest() {
+        Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer()));
+        assertEquals(NULL_VALUE_SERIALIZED, jsonb.toJson(Optional.empty()));
+    }
+
+    @Test
+    public void nullSerializerNotUsedTest() {
+        Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withNullRootSerializer(new RootNullSerializer()));
+        assertEquals("[null]", jsonb.toJson(new String[] {null}));
+    }
+
+    private static final class RootNullSerializer implements JsonbSerializer<Object> {
+
+        @Override
+        public void serialize(Object obj, JsonGenerator generator, SerializationContext ctx) {
+            generator.write(NULL_VALUE_STRING);
+        }
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/Animal.java b/src/test/java/org/eclipse/yasson/customization/model/Animal.java
new file mode 100644
index 0000000..a9218d6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/Animal.java
@@ -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 Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+public interface Animal {
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CollectionsWithFormatters.java b/src/test/java/org/eclipse/yasson/customization/model/CollectionsWithFormatters.java
new file mode 100644
index 0000000..17f8db7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CollectionsWithFormatters.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import java.util.List;
+
+import jakarta.json.bind.annotation.JsonbNumberFormat;
+
+@JsonbNumberFormat(value = "000.000", locale = "en-us")
+public class CollectionsWithFormatters {
+
+    public List<Double> doubleList;
+
+    @JsonbNumberFormat(locale = "da-da")
+    public List<Double> doubleList2;
+
+    public List<Double> doubleList3;
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CreatorConstructorPojo.java b/src/test/java/org/eclipse/yasson/customization/model/CreatorConstructorPojo.java
new file mode 100644
index 0000000..85ce720
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CreatorConstructorPojo.java
@@ -0,0 +1,38 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+import java.math.BigDecimal;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CreatorConstructorPojo {
+
+    public String str1;
+
+    public String str2;
+
+    public BigDecimal bigDec;
+
+    public CreatorFactoryMethodPojo innerFactoryCreator;
+
+    @JsonbCreator
+    public CreatorConstructorPojo(@JsonbProperty("str1") String str1, @JsonbProperty("str2") String str2) {
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CreatorFactoryMethodPojo.java b/src/test/java/org/eclipse/yasson/customization/model/CreatorFactoryMethodPojo.java
new file mode 100644
index 0000000..7be04e9
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CreatorFactoryMethodPojo.java
@@ -0,0 +1,38 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+import java.math.BigDecimal;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CreatorFactoryMethodPojo {
+
+    public final String str1;
+
+    public final String str2;
+
+    public BigDecimal bigDec;
+
+    private CreatorFactoryMethodPojo(String str1, String str2) {
+        this.str1 = str1;
+        this.str2 = str2;
+    }
+    @JsonbCreator
+    public static CreatorFactoryMethodPojo getInstance(@JsonbProperty("par1") String str1, @JsonbProperty("par2")String str2) {
+        return new CreatorFactoryMethodPojo(str1, str2);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CreatorIncompatibleTypePojo.java b/src/test/java/org/eclipse/yasson/customization/model/CreatorIncompatibleTypePojo.java
new file mode 100644
index 0000000..f5faf04
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CreatorIncompatibleTypePojo.java
@@ -0,0 +1,29 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import java.io.Serializable;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CreatorIncompatibleTypePojo {
+
+    public String s1;
+
+    @JsonbCreator
+    public static Serializable getInstance() {
+        return null;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CreatorMultipleDeclarationErrorPojo.java b/src/test/java/org/eclipse/yasson/customization/model/CreatorMultipleDeclarationErrorPojo.java
new file mode 100644
index 0000000..d80a2b4
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CreatorMultipleDeclarationErrorPojo.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CreatorMultipleDeclarationErrorPojo {
+
+    public String s1;
+
+    @JsonbCreator
+    public CreatorMultipleDeclarationErrorPojo(@JsonbProperty("val1") String s1) {
+        this.s1 = s1;
+    }
+
+    @JsonbCreator
+    public static CreatorMultipleDeclarationErrorPojo getInstance() {
+        return null;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CreatorPackagePrivateConstructor.java b/src/test/java/org/eclipse/yasson/customization/model/CreatorPackagePrivateConstructor.java
new file mode 100644
index 0000000..7f16bb0
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CreatorPackagePrivateConstructor.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CreatorPackagePrivateConstructor {
+
+    private String strVal;
+
+    private final int intVal;
+
+    @JsonbCreator
+    CreatorPackagePrivateConstructor(@JsonbProperty("intVal") int intVal) {
+        this.intVal = intVal;
+    }
+
+    public String getStrVal() {
+        return strVal;
+    }
+
+    public void setStrVal(String strVal) {
+        this.strVal = strVal;
+    }
+
+    public int getIntVal() {
+        return intVal;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJavabeanProperty.java b/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJavabeanProperty.java
new file mode 100644
index 0000000..f4702fd
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJavabeanProperty.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CreatorWithoutJavabeanProperty {
+
+    private String strField;
+
+    @JsonbCreator
+    public CreatorWithoutJavabeanProperty(@JsonbProperty("s1") String s1, @JsonbProperty("s2") String s2) {
+        this.strField = s1 + s2;
+    }
+
+    public String getStrField() {
+        return strField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJsonbProperty.java b/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJsonbProperty.java
new file mode 100644
index 0000000..f9dc9ed
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJsonbProperty.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CreatorWithoutJsonbProperty {
+
+    private final String par1;
+    private final String par2;
+    private double par3;
+
+    @JsonbCreator
+    public CreatorWithoutJsonbProperty(@JsonbProperty("s1") String par1, String par2, double d1) {
+        this.par1 = par1;
+        this.par2 = par2;
+        this.par3 = d1;
+    }
+
+    public String getPar1() {
+        return par1;
+    }
+
+    public String getPar2() {
+        return par2;
+    }
+
+    public double getPar3() {
+        return par3;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJsonbProperty1.java b/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJsonbProperty1.java
new file mode 100644
index 0000000..e3fcae4
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/CreatorWithoutJsonbProperty1.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CreatorWithoutJsonbProperty1 {
+
+    private final String par1;
+    private final String par2;
+    private byte par3;
+
+    @JsonbCreator
+    public CreatorWithoutJsonbProperty1(String par1, @JsonbProperty("s2") String par2, byte par3) {
+        this.par1 = par1;
+        this.par2 = par2;
+        this.par3 = par3;
+    }
+
+    public String getPar1() {
+        return par1;
+    }
+
+    public String getPar2() {
+        return par2;
+    }
+
+    public byte getPar3() {
+        return par3;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/DateFormatPojo.java b/src/test/java/org/eclipse/yasson/customization/model/DateFormatPojo.java
new file mode 100644
index 0000000..bc8ccac
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/DateFormatPojo.java
@@ -0,0 +1,130 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.util.Date;
+
+/**
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class DateFormatPojo {
+
+    public Date plainDateField;
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date formattedDateField;
+
+    public Date getterFormattedDateField;
+
+    public Date setterFormattedDateField;
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getterAndFieldFormattedDateField;
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date setterAndFieldFormattedDateField;
+
+    public Date getterAndSetterFormattedDateField;
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getterAndSetterAndFieldFormattedDateField;
+
+
+
+    public Date getPlainDateField() {
+        return plainDateField;
+    }
+
+    public void setPlainDateField(Date plainDateField) {
+        this.plainDateField = plainDateField;
+    }
+
+
+
+    public Date getFormattedDateField() {
+        return formattedDateField;
+    }
+
+    public void setFormattedDateField(Date formattedDateField) {
+        this.formattedDateField = formattedDateField;
+    }
+
+
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getGetterFormattedDateField() {
+        return getterFormattedDateField;
+    }
+
+    public void setGetterFormattedDateField(Date getterFormattedDateField) {
+        this.getterFormattedDateField = getterFormattedDateField;
+    }
+
+
+
+    public Date getSetterFormattedDateField() {
+        return setterFormattedDateField;
+    }
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public void setSetterFormattedDateField(Date setterFormattedDateField) {
+        this.setterFormattedDateField = setterFormattedDateField;
+    }
+
+
+
+    @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getGetterAndFieldFormattedDateField() {
+        return getterAndFieldFormattedDateField;
+    }
+
+    public void setGetterAndFieldFormattedDateField(Date getterAndFieldFormattedDateField) {
+        this.getterAndFieldFormattedDateField = getterAndFieldFormattedDateField;
+    }
+
+
+
+    public Date getSetterAndFieldFormattedDateField() {
+        return setterAndFieldFormattedDateField;
+    }
+
+    @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public void setSetterAndFieldFormattedDateField(Date setterAndFieldFormattedDateField) {
+        this.setterAndFieldFormattedDateField = setterAndFieldFormattedDateField;
+    }
+
+
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getGetterAndSetterFormattedDateField() {
+        return getterAndSetterFormattedDateField;
+    }
+
+    @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public void setGetterAndSetterFormattedDateField(Date getterAndSetterFormattedDateField) {
+        this.getterAndSetterFormattedDateField = getterAndSetterFormattedDateField;
+    }
+
+
+
+    @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getGetterAndSetterAndFieldFormattedDateField() {
+        return getterAndSetterAndFieldFormattedDateField;
+    }
+
+    @JsonbDateFormat(value = "HH:mm:ss $$ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public void setGetterAndSetterAndFieldFormattedDateField(Date getterAndSetterAndFieldFormattedDateField) {
+        this.getterAndSetterAndFieldFormattedDateField = getterAndSetterAndFieldFormattedDateField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/DateFormatPojoWithClassLevelFormatter.java b/src/test/java/org/eclipse/yasson/customization/model/DateFormatPojoWithClassLevelFormatter.java
new file mode 100644
index 0000000..fd5848c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/DateFormatPojoWithClassLevelFormatter.java
@@ -0,0 +1,133 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.util.Date;
+
+/**
+ * The pojo class that used to test {@link JsonbDateFormat} when the containing class has a default {@link JsonbDateFormat} defined
+ *
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+@JsonbDateFormat(value = "HH:mm:ss ^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+public class DateFormatPojoWithClassLevelFormatter {
+
+    public Date plainDateField;
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date formattedDateField;
+
+    public Date getterFormattedDateField;
+
+    public Date setterFormattedDateField;
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getterAndFieldFormattedDateField;
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date setterAndFieldFormattedDateField;
+
+    public Date getterAndSetterFormattedDateField;
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getterAndSetterAndFieldFormattedDateField;
+
+
+
+    public Date getPlainDateField() {
+        return plainDateField;
+    }
+
+    public void setPlainDateField(Date plainDateField) {
+        this.plainDateField = plainDateField;
+    }
+
+
+
+    public Date getFormattedDateField() {
+        return formattedDateField;
+    }
+
+    public void setFormattedDateField(Date formattedDateField) {
+        this.formattedDateField = formattedDateField;
+    }
+
+
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getGetterFormattedDateField() {
+        return getterFormattedDateField;
+    }
+
+    public void setGetterFormattedDateField(Date getterFormattedDateField) {
+        this.getterFormattedDateField = getterFormattedDateField;
+    }
+
+
+
+    public Date getSetterFormattedDateField() {
+        return setterFormattedDateField;
+    }
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public void setSetterFormattedDateField(Date setterFormattedDateField) {
+        this.setterFormattedDateField = setterFormattedDateField;
+    }
+
+
+
+    @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getGetterAndFieldFormattedDateField() {
+        return getterAndFieldFormattedDateField;
+    }
+
+    public void setGetterAndFieldFormattedDateField(Date getterAndFieldFormattedDateField) {
+        this.getterAndFieldFormattedDateField = getterAndFieldFormattedDateField;
+    }
+
+
+
+    public Date getSetterAndFieldFormattedDateField() {
+        return setterAndFieldFormattedDateField;
+    }
+
+    @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public void setSetterAndFieldFormattedDateField(Date setterAndFieldFormattedDateField) {
+        this.setterAndFieldFormattedDateField = setterAndFieldFormattedDateField;
+    }
+
+
+
+    @JsonbDateFormat(value = "HH:mm:ss ^^ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getGetterAndSetterFormattedDateField() {
+        return getterAndSetterFormattedDateField;
+    }
+
+    @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public void setGetterAndSetterFormattedDateField(Date getterAndSetterFormattedDateField) {
+        this.getterAndSetterFormattedDateField = getterAndSetterFormattedDateField;
+    }
+
+
+
+    @JsonbDateFormat(value = "HH:mm:ss <> dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public Date getGetterAndSetterAndFieldFormattedDateField() {
+        return getterAndSetterAndFieldFormattedDateField;
+    }
+
+    @JsonbDateFormat(value = "HH:mm:ss $$ dd-MM-yyyy", locale = "Europe/Copenhagen")
+    public void setGetterAndSetterAndFieldFormattedDateField(Date getterAndSetterAndFieldFormattedDateField) {
+        this.getterAndSetterAndFieldFormattedDateField = getterAndSetterAndFieldFormattedDateField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/Dog.java b/src/test/java/org/eclipse/yasson/customization/model/Dog.java
new file mode 100644
index 0000000..1c30a4d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/Dog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+public class Dog implements Animal {
+
+    private String dogProperty;
+
+    public Dog() {
+    }
+
+    public Dog(String dogProperty) {
+        this.dogProperty = dogProperty;
+    }
+
+    public String getDogProperty() {
+        return dogProperty;
+    }
+
+    public void setDogProperty(String dogProperty) {
+        this.dogProperty = dogProperty;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/FieldCustomOrder.java b/src/test/java/org/eclipse/yasson/customization/model/FieldCustomOrder.java
new file mode 100644
index 0000000..1f1ed72
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/FieldCustomOrder.java
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbPropertyOrder;
+
+/**
+ * @author David Kral
+ */
+@JsonbPropertyOrder({"aField", "cField", "dField" ,"bField"})
+public class FieldCustomOrder {
+
+    public String aField = "aValue";
+    public String dField = "dValue";
+    public String bField = "bValue";
+    public String cField = "cValue";
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/FieldCustomOrderWrapper.java b/src/test/java/org/eclipse/yasson/customization/model/FieldCustomOrderWrapper.java
new file mode 100644
index 0000000..857f6d0
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/FieldCustomOrderWrapper.java
@@ -0,0 +1,24 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+/**
+ * @author David Kral
+ */
+public class FieldCustomOrderWrapper {
+
+    public String stringField = "stringValue";
+    public int intField = 1;
+    public FieldCustomOrder fieldCustomOrder = new FieldCustomOrder();
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/FieldOrder.java b/src/test/java/org/eclipse/yasson/customization/model/FieldOrder.java
new file mode 100644
index 0000000..e9016df
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/FieldOrder.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+/**
+ * @author David Kral
+ */
+public class FieldOrder {
+
+    public String aField = "aValue";
+    public String dField = "dValue";
+    public String bField = "bValue";
+    public String cField = "cValue";
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/FieldOrderNameAnnotation.java b/src/test/java/org/eclipse/yasson/customization/model/FieldOrderNameAnnotation.java
new file mode 100644
index 0000000..c3cc591
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/FieldOrderNameAnnotation.java
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author David Kral
+ */
+public class FieldOrderNameAnnotation {
+
+    @JsonbProperty(value = "zField")
+    public String aField = "aValue";
+    public String dField = "dValue";
+    public String bField = "bValue";
+    public String cField = "cValue";
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/FieldSpecificOrder.java b/src/test/java/org/eclipse/yasson/customization/model/FieldSpecificOrder.java
new file mode 100644
index 0000000..7fa5acf
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/FieldSpecificOrder.java
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbPropertyOrder;
+
+/**
+ * @author David Kral
+ */
+@JsonbPropertyOrder({"aField", "dField"})
+public class FieldSpecificOrder {
+
+    public String aField = "aValue";
+    public String dField = "dValue";
+    public String bField = "bValue";
+    public String cField = "cValue";
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/ImplementationClassPojo.java b/src/test/java/org/eclipse/yasson/customization/model/ImplementationClassPojo.java
new file mode 100644
index 0000000..a7aeb75
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/ImplementationClassPojo.java
@@ -0,0 +1,29 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import org.eclipse.yasson.ImplementationClass;
+
+public class ImplementationClassPojo {
+
+    @ImplementationClass(Dog.class)
+    private Animal animal;
+
+    public Animal getAnimal() {
+        return animal;
+    }
+
+    public void setAnimal(Animal animal) {
+        this.animal = animal;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InheritedAnnotationsPojo.java b/src/test/java/org/eclipse/yasson/customization/model/InheritedAnnotationsPojo.java
new file mode 100644
index 0000000..2dfeae8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InheritedAnnotationsPojo.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@InheritsNillableRecursion
+public class InheritedAnnotationsPojo {
+
+    @InheritsJsonbProperty
+    public String property;
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InheritsJsonbProperty.java b/src/test/java/org/eclipse/yasson/customization/model/InheritsJsonbProperty.java
new file mode 100644
index 0000000..b14d81c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InheritsJsonbProperty.java
@@ -0,0 +1,26 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@JsonbProperty("renamedProperty")
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
+public @interface InheritsJsonbProperty {
+    String value() default "abc";
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InheritsNillable.java b/src/test/java/org/eclipse/yasson/customization/model/InheritsNillable.java
new file mode 100644
index 0000000..28b92d3
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InheritsNillable.java
@@ -0,0 +1,27 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbNillable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+
+@JsonbNillable()
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ANNOTATION_TYPE, TYPE, PACKAGE})
+public @interface InheritsNillable {
+    String value() default "abc";
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InheritsNillableRecursion.java b/src/test/java/org/eclipse/yasson/customization/model/InheritsNillableRecursion.java
new file mode 100644
index 0000000..0297730
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InheritsNillableRecursion.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+
+@InheritsNillable
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ANNOTATION_TYPE, TYPE, PACKAGE})
+public @interface InheritsNillableRecursion {
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoA.java b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoA.java
new file mode 100644
index 0000000..ec7389b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoA.java
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public interface InterfacedPojoA {
+
+
+    @JsonbProperty("propA")
+    String getPropertyA();
+
+    @JsonbProperty("propA")
+    void setPropertyA(String property);
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoAbsImpl.java b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoAbsImpl.java
new file mode 100644
index 0000000..3fd2f1e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoAbsImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public abstract class InterfacedPojoAbsImpl implements InterfacedPojoA {
+
+    private String propertyA;
+
+    @Override
+    public String getPropertyA() {
+        return propertyA;
+    }
+
+    @Override
+    public void setPropertyA(String property) {
+        this.propertyA = property;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoB.java b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoB.java
new file mode 100644
index 0000000..86fdf6b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoB.java
@@ -0,0 +1,27 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public interface InterfacedPojoB extends InterfacedPojoA {
+
+    @JsonbProperty("propB")
+    String getPropertyB();
+
+    @JsonbProperty("propB")
+    void setPropertyB(String propertyB);
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoC.java b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoC.java
new file mode 100644
index 0000000..827ef8c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoC.java
@@ -0,0 +1,27 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public interface InterfacedPojoC extends InterfacedPojoB {
+
+    @JsonbProperty("propC")
+    String getPropertyC();
+
+    @JsonbProperty("propC")
+    void setPropertyC(String propertyC);
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoImpl.java b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoImpl.java
new file mode 100644
index 0000000..f3693a0
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/InterfacedPojoImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class InterfacedPojoImpl extends InterfacedPojoAbsImpl implements InterfacedPojoB {
+
+    private String propertyB;
+
+    @Override
+    public String getPropertyB() {
+        return propertyB;
+    }
+
+    @Override
+    public void setPropertyB(String propertyB) {
+        this.propertyB = propertyB;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableClassFirstLevel.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableClassFirstLevel.java
new file mode 100644
index 0000000..306f905
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableClassFirstLevel.java
@@ -0,0 +1,31 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+/**
+ * Implements a class with nillable annotation.
+ *
+ * @author Roman Grigoriadi
+ */
+public class JsonbNillableClassFirstLevel implements JsonbNillableInterfaceBaseOne {
+
+    private String classNillable;
+
+    public String getClassNillable() {
+        return classNillable;
+    }
+
+    public void setClassNillable(String classNillable) {
+        this.classNillable = classNillable;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableClassSecondLevel.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableClassSecondLevel.java
new file mode 100644
index 0000000..01c61e4
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableClassSecondLevel.java
@@ -0,0 +1,31 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+/**
+ * Extends a class which implements interface which is nillable.
+ *
+ * @author Roman Grigoriadi
+ */
+public class JsonbNillableClassSecondLevel extends JsonbNillableClassFirstLevel {
+
+    private String subclassNillable;
+
+    public String getSubclassNillable() {
+        return subclassNillable;
+    }
+
+    public void setSubclassNillable(String subclassNillable) {
+        this.subclassNillable = subclassNillable;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBase.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBase.java
new file mode 100644
index 0000000..4e50ca3
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBase.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbNillable;
+
+/**
+ * All extending classes should be nillable.
+ * @author Roman Grigoriadi
+ */
+@JsonbNillable
+public interface JsonbNillableInterfaceBase {
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBaseOne.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBaseOne.java
new file mode 100644
index 0000000..df70990
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBaseOne.java
@@ -0,0 +1,19 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public interface JsonbNillableInterfaceBaseOne extends JsonbNillableInterfaceBase {
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBaseTwo.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBaseTwo.java
new file mode 100644
index 0000000..cf660a1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableInterfaceBaseTwo.java
@@ -0,0 +1,24 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbNillable;
+
+/**
+ * Overrides super interface nillable value.
+ *
+ * @author Roman Grigoriadi
+ */
+@JsonbNillable(false)
+public interface JsonbNillableInterfaceBaseTwo extends JsonbNillableInterfaceBase {
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverriddenWithJsonbProperty.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverriddenWithJsonbProperty.java
new file mode 100644
index 0000000..4baadb8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverriddenWithJsonbProperty.java
@@ -0,0 +1,54 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbNillableOverriddenWithJsonbProperty {
+
+    @JsonbProperty("nillableOverriddenAccidentallyWithDefaultValue")
+    private String nillableOverriddenWithFieldJsonbProperty;
+
+    private String nillableOverriddenWithGetterJsonbProperty;
+
+    private String nillableOverriddenWithSetterJsonbProperty;
+
+    public String getNillableOverriddenWithFieldJsonbProperty() {
+        return nillableOverriddenWithFieldJsonbProperty;
+    }
+
+    public void setNillableOverriddenWithFieldJsonbProperty(String nillableOverriddenWithFieldJsonbProperty) {
+        this.nillableOverriddenWithFieldJsonbProperty = nillableOverriddenWithFieldJsonbProperty;
+    }
+
+    @JsonbProperty(nillable = false)
+    public String getNillableOverriddenWithGetterJsonbProperty() {
+        return nillableOverriddenWithGetterJsonbProperty;
+    }
+
+    public void setNillableOverriddenWithGetterJsonbProperty(String nillableOverriddenWithGetterJsonbProperty) {
+        this.nillableOverriddenWithGetterJsonbProperty = nillableOverriddenWithGetterJsonbProperty;
+    }
+
+    public String getNillableOverriddenWithSetterJsonbProperty() {
+        return nillableOverriddenWithSetterJsonbProperty;
+    }
+
+    @JsonbProperty(nillable = false)
+    public void setNillableOverriddenWithSetterJsonbProperty(String nillableOverriddenWithSetterJsonbProperty) {
+        this.nillableOverriddenWithSetterJsonbProperty = nillableOverriddenWithSetterJsonbProperty;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverridesClass.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverridesClass.java
new file mode 100644
index 0000000..babe880
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverridesClass.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbNillable;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbNillable
+public class JsonbNillableOverridesClass  extends JsonbNillableOverridesInterface {
+    @JsonbProperty("overridesNillableInParent")
+    public String nillableField;
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverridesInterface.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverridesInterface.java
new file mode 100644
index 0000000..42faf8b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableOverridesInterface.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbNillable;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbNillable(false)
+public class JsonbNillableOverridesInterface implements JsonbNillableInterfaceBaseTwo {
+    public String nillableField;
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableValue.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableValue.java
new file mode 100644
index 0000000..9d61114
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbNillableValue.java
@@ -0,0 +1,36 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbNillable;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbNillable
+public class JsonbNillableValue {
+
+    private String nillableField;
+
+    private String privateField;
+
+    private static String staticField;
+
+    public String getNillableField() {
+        return nillableField;
+    }
+
+    public void setNillableField(String nillableField) {
+        this.nillableField = nillableField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyName.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyName.java
new file mode 100644
index 0000000..1056f62
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyName.java
@@ -0,0 +1,57 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbPropertyName {
+
+    @JsonbProperty("fieldAnnotatedNameCustomized")
+    private String fieldAnnotatedName;
+
+    private String methodAnnotName;
+
+    @JsonbProperty("fieldAnnotatedNameToOverride")
+    private String fieldOverriddenWithMethodAnnot;
+
+    public String getFieldAnnotatedName() {
+        return fieldAnnotatedName;
+    }
+
+    public void setFieldAnnotatedName(String fieldAnnotatedName) {
+        this.fieldAnnotatedName = fieldAnnotatedName;
+    }
+
+    @JsonbProperty("getterAnnotatedName")
+    public String getMethodAnnotName() {
+        return methodAnnotName;
+    }
+
+    @JsonbProperty("setterAnnotatedName")
+    public void setMethodAnnotName(String methodAnnotName) {
+        this.methodAnnotName = methodAnnotName;
+    }
+
+    @JsonbProperty("getterOverriddenName")
+    public String getFieldOverriddenWithMethodAnnot() {
+        return fieldOverriddenWithMethodAnnot;
+    }
+
+    @JsonbProperty("setterOverriddenName")
+    public void setFieldOverriddenWithMethodAnnot(String fieldOverriddenWithMethodAnnot) {
+        this.fieldOverriddenWithMethodAnnot = fieldOverriddenWithMethodAnnot;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyNameCollision.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyNameCollision.java
new file mode 100644
index 0000000..67d7e8c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyNameCollision.java
@@ -0,0 +1,42 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbPropertyNameCollision {
+
+    private String pojoName;
+
+    @JsonbProperty("pojoName")
+    private String pojoNameCollision;
+
+    public String getPojoName() {
+        return pojoName;
+    }
+
+    public void setPojoName(String pojoName) {
+        this.pojoName = pojoName;
+    }
+
+    public String getPojoNameCollision() {
+        return pojoNameCollision;
+    }
+
+    public void setPojoNameCollision(String pojoNameCollision) {
+        this.pojoNameCollision = pojoNameCollision;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyNillable.java b/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyNillable.java
new file mode 100644
index 0000000..f6142ac
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/JsonbPropertyNillable.java
@@ -0,0 +1,44 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbPropertyNillable {
+
+    @JsonbProperty(nillable = true)
+    private String nullField;
+
+    @JsonbProperty(nillable = false)
+    private String nillableOverride;
+
+    public String getNullField() {
+        return nullField;
+    }
+
+    public void setNullField(String nullField) {
+        this.nullField = nullField;
+    }
+
+    @JsonbProperty(nillable = true)
+    public String getNillableOverride() {
+        return nillableOverride;
+    }
+
+    public void setNillableOverride(String nillableOverride) {
+        this.nillableOverride = nillableOverride;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/NumberFormatPojo.java b/src/test/java/org/eclipse/yasson/customization/model/NumberFormatPojo.java
new file mode 100644
index 0000000..027a372
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/NumberFormatPojo.java
@@ -0,0 +1,81 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbNumberFormat;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbNumberFormat("0.0")
+public class NumberFormatPojo {
+
+    @JsonbNumberFormat("00000000.000000")
+    public BigDecimal bigDecimal;
+
+    @JsonbNumberFormat("00000000")
+    public BigInteger bigInteger;
+
+    @JsonbNumberFormat("000.00000000")
+    public Double aDouble;
+
+    @JsonbNumberFormat("000.00000000")
+    public Float aFloat;
+
+    @JsonbNumberFormat("00000000000")
+    public Long aLong;
+
+    public Integer integer;
+
+    @JsonbNumberFormat("00000")
+    public Short aShort;
+
+    @JsonbNumberFormat("000")
+    public Byte aByte;
+
+    private Double doubleGetterFormatted;
+
+    private Double doubleSetterFormatted;
+
+    @JsonbNumberFormat(value = "000.000", locale = "en-us")
+    private Double doubleSetterAndPropertyFormatter;
+
+    @JsonbNumberFormat("000.00000000")
+    public Double getDoubleGetterFormatted() {
+        return doubleGetterFormatted;
+    }
+
+    public void setDoubleGetterFormatted(Double doubleGetterFormatted) {
+        this.doubleGetterFormatted = doubleGetterFormatted;
+    }
+
+    public Double getDoubleSetterFormatted() {
+        return doubleSetterFormatted;
+    }
+
+    @JsonbNumberFormat(value = "000,000", locale = "da-da")
+    public void setDoubleSetterFormatted(Double doubleSetterFormatted) {
+        this.doubleSetterFormatted = doubleSetterFormatted;
+    }
+
+    public Double getDoubleSetterAndPropertyFormatter() {
+        return doubleSetterAndPropertyFormatter;
+    }
+
+    @JsonbNumberFormat(value = "000,000", locale = "da-da")
+    public void setDoubleSetterAndPropertyFormatter(Double doubleSetterAndPropertyFormatter) {
+        this.doubleSetterAndPropertyFormatter = doubleSetterAndPropertyFormatter;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/NumberFormatPojoWithoutClassLevelFormatter.java b/src/test/java/org/eclipse/yasson/customization/model/NumberFormatPojoWithoutClassLevelFormatter.java
new file mode 100644
index 0000000..25e3531
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/NumberFormatPojoWithoutClassLevelFormatter.java
@@ -0,0 +1,54 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbNumberFormat;
+
+/**
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class NumberFormatPojoWithoutClassLevelFormatter {
+    private Double doubleGetterFormatted;
+
+    private Double doubleSetterFormatted;
+
+    @JsonbNumberFormat(value = "000.000", locale = "en-us")
+    private Double doubleSetterAndPropertyFormatter;
+
+    @JsonbNumberFormat("000.00000000")
+    public Double getDoubleGetterFormatted() {
+        return doubleGetterFormatted;
+    }
+
+    public void setDoubleGetterFormatted(Double doubleGetterFormatted) {
+        this.doubleGetterFormatted = doubleGetterFormatted;
+    }
+
+    public Double getDoubleSetterFormatted() {
+        return doubleSetterFormatted;
+    }
+
+    @JsonbNumberFormat(value = "000,000", locale = "da-da")
+    public void setDoubleSetterFormatted(Double doubleSetterFormatted) {
+        this.doubleSetterFormatted = doubleSetterFormatted;
+    }
+
+    public Double getDoubleSetterAndPropertyFormatter() {
+        return doubleSetterAndPropertyFormatter;
+    }
+
+    @JsonbNumberFormat(value = "000,000", locale = "da-da")
+    public void setDoubleSetterAndPropertyFormatter(Double doubleSetterAndPropertyFormatter) {
+        this.doubleSetterAndPropertyFormatter = doubleSetterAndPropertyFormatter;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/ParameterNameTester.java b/src/test/java/org/eclipse/yasson/customization/model/ParameterNameTester.java
new file mode 100644
index 0000000..8f09727
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/ParameterNameTester.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+import java.net.URI;
+
+/**
+ *
+ * @author David Kral
+ */
+public class ParameterNameTester {
+    @JsonbProperty("string")
+    public final String name;
+    @JsonbProperty("someParam")
+    public final String secondParam;
+
+    @JsonbCreator
+    public ParameterNameTester(@JsonbProperty("string") String name, @JsonbProperty("someParam") String secondParam) {
+        this.name = name;
+        this.secondParam = secondParam;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/RenamedPropertiesContainer.java b/src/test/java/org/eclipse/yasson/customization/model/RenamedPropertiesContainer.java
new file mode 100644
index 0000000..2d3562c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/RenamedPropertiesContainer.java
@@ -0,0 +1,58 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/*
+ * $Id$
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+public class RenamedPropertiesContainer {
+    @JsonbProperty("first")
+    private int intInstance;
+    @JsonbProperty("second")
+    private String stringInstance;
+    @JsonbProperty("third")
+    private long longInstance;
+
+    public String getStringInstance() {
+        return stringInstance;
+    }
+
+    public void setStringInstance(String stringInstance) {
+        this.stringInstance = stringInstance;
+        if (intInstance == 1) {
+            intInstance = 2;
+        }
+    }
+
+    public int getIntInstance() {
+        return intInstance;
+    }
+
+    public void setIntInstance(int intInstance) {
+        this.intInstance = intInstance;
+    }
+
+    public long getLongInstance() {
+        return longInstance;
+    }
+
+    public void setLongInstance(long longInstance) {
+        this.longInstance = longInstance;
+        if (intInstance == 2) {
+            intInstance = 3;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/TrimmedDatePojo.java b/src/test/java/org/eclipse/yasson/customization/model/TrimmedDatePojo.java
new file mode 100644
index 0000000..e1b8b04
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/TrimmedDatePojo.java
@@ -0,0 +1,112 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
+
+public class TrimmedDatePojo {
+
+    @JsonbDateFormat(value = "yyyy.MM.dd")
+    private Date date;
+
+    @JsonbDateFormat(value = "yyyy.MM.dd")
+    private Calendar calendar;
+
+    @JsonbDateFormat(value = "yyyy.MM.dd")
+    private LocalDateTime localDateTime;
+
+    @JsonbDateFormat(value = "yyyy.MM.dd")
+    private ZonedDateTime zonedDateTime;
+
+
+    @JsonbDateFormat(value = "yyyy.MM.dd HH:ss")
+    private ZonedDateTime zonedDateTimeHoursAndSeconds;
+
+    @JsonbDateFormat(value = "yyyy.MM.dd N")
+    private ZonedDateTime zonedDateTimeNanosOfDay;
+
+    @JsonbDateFormat(value = "yyyy.MM.dd VV")
+    private ZonedDateTime zonedDateTimeOverriddenZone;
+
+    @JsonbDateFormat(value = "yyyy.MM.dd VV")
+    private Instant zonedInstant;
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public LocalDateTime getLocalDateTime() {
+        return localDateTime;
+    }
+
+    public void setLocalDateTime(LocalDateTime localDateTime) {
+        this.localDateTime = localDateTime;
+    }
+
+    public ZonedDateTime getZonedDateTime() {
+        return zonedDateTime;
+    }
+
+    public void setZonedDateTime(ZonedDateTime zonedDateTime) {
+        this.zonedDateTime = zonedDateTime;
+    }
+
+    public ZonedDateTime getZonedDateTimeNanosOfDay() {
+        return zonedDateTimeNanosOfDay;
+    }
+
+    public void setZonedDateTimeNanosOfDay(ZonedDateTime zonedDateTimeNanosOfDay) {
+        this.zonedDateTimeNanosOfDay = zonedDateTimeNanosOfDay;
+    }
+
+    public ZonedDateTime getZonedDateTimeHoursAndSeconds() {
+        return zonedDateTimeHoursAndSeconds;
+    }
+
+    public void setZonedDateTimeHoursAndSeconds(ZonedDateTime zonedDateTimeHoursAndSeconds) {
+        this.zonedDateTimeHoursAndSeconds = zonedDateTimeHoursAndSeconds;
+    }
+
+    public ZonedDateTime getZonedDateTimeOverriddenZone() {
+        return zonedDateTimeOverriddenZone;
+    }
+
+    public void setZonedDateTimeOverriddenZone(ZonedDateTime zonedDateTimeOverriddenZone) {
+        this.zonedDateTimeOverriddenZone = zonedDateTimeOverriddenZone;
+    }
+
+    public Instant getZonedInstant() {
+        return zonedInstant;
+    }
+
+    public void setZonedInstant(Instant zonedInstant) {
+        this.zonedInstant = zonedInstant;
+    }
+
+    public Calendar getCalendar() {
+        return calendar;
+    }
+
+    public void setCalendar(Calendar calendar) {
+        this.calendar = calendar;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/JsonbNillablePackageLevel.java b/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/JsonbNillablePackageLevel.java
new file mode 100644
index 0000000..4f27c2f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/JsonbNillablePackageLevel.java
@@ -0,0 +1,29 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model.packagelevelannotations;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbNillablePackageLevel {
+
+    private String packageLevelNillableField;
+
+    public String getPackageLevelNillableField() {
+        return packageLevelNillableField;
+    }
+
+    public void setPackageLevelNillableField(String packageLevelNillableField) {
+        this.packageLevelNillableField = packageLevelNillableField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/PackageLevelOverriddenWithClassLevel.java b/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/PackageLevelOverriddenWithClassLevel.java
new file mode 100644
index 0000000..13e6322
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/PackageLevelOverriddenWithClassLevel.java
@@ -0,0 +1,32 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.model.packagelevelannotations;
+
+import jakarta.json.bind.annotation.JsonbNillable;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbNillable(false)
+public class PackageLevelOverriddenWithClassLevel {
+
+    private String nillableOverriddenField;
+
+    public String getNillableOverriddenField() {
+        return nillableOverriddenField;
+    }
+
+    public void setNillableOverriddenField(String nillableOverriddenField) {
+        this.nillableOverriddenField = nillableOverriddenField;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/package-info.java b/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/package-info.java
new file mode 100644
index 0000000..056e58a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/model/packagelevelannotations/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbNillable
+package org.eclipse.yasson.customization.model.packagelevelannotations;
+
+import jakarta.json.bind.annotation.JsonbNillable;
diff --git a/src/test/java/org/eclipse/yasson/customization/polymorphism/AnnotationPolymorphismTest.java b/src/test/java/org/eclipse/yasson/customization/polymorphism/AnnotationPolymorphismTest.java
new file mode 100644
index 0000000..bf6ae1b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/polymorphism/AnnotationPolymorphismTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.polymorphism;
+
+import java.time.LocalDate;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbSubtype;
+import jakarta.json.bind.annotation.JsonbTypeInfo;
+
+import org.eclipse.yasson.Jsonbs;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Tests for verification of proper polymorphism handling based on annotation.
+ */
+public class AnnotationPolymorphismTest {
+
+    public static final String ARRAY_EXPECTED = "[{\"@type\":\"dog\",\"isDog\":true},{\"@type\":\"cat\",\"isCat\":true},"
+            + "{\"@type\":\"dog\",\"isDog\":true}]";
+
+    @Test
+    public void testBasicSerialization() {
+        Dog dog = new Dog();
+        assertThat(Jsonbs.defaultJsonb.toJson(dog), is("{\"@type\":\"dog\",\"isDog\":true}"));
+        Cat cat = new Cat();
+        assertThat(Jsonbs.defaultJsonb.toJson(cat), is("{\"@type\":\"cat\",\"isCat\":true}"));
+    }
+
+    @Test
+    public void testBasicDeserialization() {
+        Animal dog = Jsonbs.defaultJsonb.fromJson("{\"@type\":\"dog\",\"isDog\":false}", Animal.class);
+        assertThat(dog, instanceOf(Dog.class));
+        assertThat(((Dog) dog).isDog, is(false));
+        Animal cat = Jsonbs.defaultJsonb.fromJson("{\"@type\":\"cat\",\"isCat\":false}", Animal.class);
+        assertThat(cat, instanceOf(Cat.class));
+        assertThat(((Cat) cat).isCat, is(false));
+    }
+
+    @Test
+    public void testExactTypeDeserialization() {
+        Dog dog = Jsonbs.defaultJsonb.fromJson("{\"isDog\":false}", Dog.class);
+        assertThat(dog.isDog, is(false));
+        dog = Jsonbs.defaultJsonb.fromJson("{\"@type\":\"dog\", \"isDog\":false}", Dog.class);
+        assertThat(dog.isDog, is(false));
+    }
+
+    @Test
+    public void testUnknownAliasDeserialization() {
+        JsonbException exception = assertThrows(JsonbException.class,
+                                                () -> Jsonbs.defaultJsonb.fromJson("{\"@type\":\"rat\",\"isDog\":false}",
+                                                                                   Animal.class));
+        assertThat(exception.getMessage(), startsWith("Unknown alias \"rat\" of the type org.eclipse.yasson.customization."
+                                                              + "polymorphism.AnnotationPolymorphismTest$Animal. Known aliases: ["));
+    }
+
+    @Test
+    public void testUnknownAliasSerialization() {
+        Rat rat = new Rat();
+        assertThat(Jsonbs.defaultJsonb.toJson(rat), is("{\"isRat\":true}"));
+    }
+
+    @Test
+    public void testCreatorDeserialization() {
+        SomeDateType creator = Jsonbs.defaultJsonb
+                .fromJson("{\"@dateType\":\"constructor\",\"localDate\":\"26-02-2021\"}", SomeDateType.class);
+        assertThat(creator, instanceOf(DateConstructor.class));
+    }
+
+    @Test
+    public void testArraySerialization() {
+        Animal[] animals = new Animal[] {new Dog(), new Cat(), new Dog()};
+        assertThat(Jsonbs.defaultJsonb.toJson(animals), is(ARRAY_EXPECTED));
+    }
+
+    @Test
+    public void testArrayDeserialization() {
+        Animal[] deserialized = Jsonbs.defaultJsonb.fromJson(ARRAY_EXPECTED, Animal[].class);
+        assertThat(deserialized.length, is(3));
+        assertThat(deserialized[0], instanceOf(Dog.class));
+        assertThat(deserialized[1], instanceOf(Cat.class));
+        assertThat(deserialized[2], instanceOf(Dog.class));
+    }
+
+    @JsonbTypeInfo({
+            @JsonbSubtype(alias = "dog", type = Dog.class),
+            @JsonbSubtype(alias = "cat", type = Cat.class)
+    })
+    public interface Animal {
+
+    }
+
+    public static class Dog implements Animal {
+
+        public boolean isDog = true;
+
+    }
+
+    public static class Cat implements Animal {
+
+        public boolean isCat = true;
+
+    }
+
+    public static class Rat implements Animal {
+
+        public boolean isRat = true;
+
+    }
+
+    @JsonbTypeInfo(key = "@dateType", value = {
+            @JsonbSubtype(alias = "constructor", type = DateConstructor.class)
+    })
+    public interface SomeDateType {
+
+    }
+
+    public static final class DateConstructor implements SomeDateType {
+
+        public LocalDate localDate;
+
+        @JsonbCreator
+        public DateConstructor(@JsonbProperty("localDate") @JsonbDateFormat(value = "dd-MM-yyyy", locale = "nl-NL") LocalDate localDate) {
+            this.localDate = localDate;
+        }
+
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/polymorphism/MultiplePolymorphicInfoTest.java b/src/test/java/org/eclipse/yasson/customization/polymorphism/MultiplePolymorphicInfoTest.java
new file mode 100644
index 0000000..9bb575a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/polymorphism/MultiplePolymorphicInfoTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.polymorphism;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.annotation.JsonbSubtype;
+import jakarta.json.bind.annotation.JsonbTypeInfo;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * TODO javadoc
+ */
+public class MultiplePolymorphicInfoTest {
+
+    private static final Jsonb JSONB = JsonbBuilder.create();
+
+    @Test
+    public void testMultiplePolymorphicInfoPropertySerialization() {
+        String expected = "{\"@something\":\"animal\",\"@animal\":\"dog\",\"@dogRace\":\"labrador\",\"isLabrador\":true}";
+        Labrador labrador = new Labrador();
+        assertThat(JSONB.toJson(labrador), is(expected));
+    }
+
+    @Test
+    public void testMultiplePolymorphicInfoPropertyDeserialization() {
+        String json = "{\"@something\":\"animal\",\"@animal\":\"dog\",\"@dogRace\":\"labrador\",\"isLabrador\":true}";
+        assertThat(JSONB.fromJson(json, Labrador.class), instanceOf(Labrador.class));
+    }
+
+    @Test
+    public void testPolymorphicParentInstanceSerialization() {
+        String expected = "{\"@type\":\"area\",\"name\":\"North America\",\"population\":600000000}";
+        Area northAmerica = new Area();
+        northAmerica.name = "North America";
+        northAmerica.population = 600000000;
+        assertThat(JSONB.toJson(northAmerica), is(expected));
+    }
+
+    @Test
+    public void testPolymorphicParentInstanceDeserialization() {
+        String json = "{\"@type\":\"area\",\"name\":\"North America\",\"population\":600000000}";
+        assertThat(JSONB.fromJson(json, Location.class), instanceOf(Area.class));
+    }
+
+    @JsonbTypeInfo(key = "@something", value = {
+            @JsonbSubtype(alias = "animal", type = Animal.class)
+    })
+    public interface Something { }
+
+    @JsonbTypeInfo(key = "@animal", value = {
+            @JsonbSubtype(alias = "dog", type = Dog.class)
+    })
+    public interface Animal extends Something {
+    }
+
+    @JsonbTypeInfo(key = "@dogRace", value = {
+            @JsonbSubtype(alias = "labrador", type = Labrador.class)
+    })
+    public interface Dog extends Animal {
+    }
+
+    public static class Labrador implements Dog {
+
+        public boolean isLabrador = true;
+
+    }
+
+    @JsonbTypeInfo({
+            @JsonbSubtype(alias = "area", type = Area.class)
+    })
+    public interface Location {
+    }
+
+    @JsonbTypeInfo(key = "@area", value = {
+            @JsonbSubtype(alias = "city", type = City.class),
+            @JsonbSubtype(alias = "state", type = State.class)
+    })
+    public static class Area implements Location {
+        public String name;
+        public long population;
+    }
+
+    public static class City extends Area {
+        public String state;
+    }
+
+    public static class State extends Area {
+        public String capital;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/polymorphism/NestedPolymorphismTest.java b/src/test/java/org/eclipse/yasson/customization/polymorphism/NestedPolymorphismTest.java
new file mode 100644
index 0000000..fe1c4dd
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/polymorphism/NestedPolymorphismTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2021, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.polymorphism;
+
+import jakarta.json.bind.annotation.JsonbSubtype;
+import jakarta.json.bind.annotation.JsonbTypeInfo;
+
+import org.eclipse.yasson.Jsonbs;
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Tests for verification of proper polymorphism handling based on annotation.
+ */
+public class NestedPolymorphismTest {
+
+    /**
+     * 1st test for: https://github.com/eclipse-ee4j/yasson/issues/589
+     * <p>(deserialization of nested polymorphic and unmapped properties)
+     */
+    @Test
+    public void testNestedUnmappedProperty() {
+        String json = "{\"inner\":{\"id\":123,\"@type\":\"derivationA\","
+                + "\"unmapped\":{\"x\":9,\"y\":[9,8,7]},\"name\":\"abc\"}}";
+        Outer obj = assertDoesNotThrow(() -> defaultJsonb.fromJson(json, Outer.class));
+        assertEquals(123L, obj.inner.id);
+        assertEquals("abc", obj.inner.name);
+    }
+
+    // a base class
+    @JsonbTypeInfo(key = "@type", value =
+            @JsonbSubtype(type = InnerBase.class, alias = "derivationA"))
+    public static class InnerBase {
+        public Long id;
+        public String name;
+    }
+
+    // derivation of the base class
+    public class Derivation extends InnerBase {}
+
+    // an arbitrary 'outer' root element
+    public static class Outer {
+        public InnerBase inner;
+    }
+
+    /**
+     * 2nd test for: https://github.com/eclipse-ee4j/yasson/issues/589
+     * <p>(deserialization of multiple nested polymorphic properties)
+     */
+    @Test
+    public void testNestedDeserialization() {
+        String json = "{\"@type\":\"Pets\",\"pet1\":{\"@type\":\"Cat\",\"name\":\"kitty\"}"
+                + ",\"pet2\":{\"@type\":\"Dog\",\"name\":\"puppy\"}}";
+        final Animals animals = Jsonbs.defaultJsonb.fromJson(json, Animals.class);
+        assertThat(animals, instanceOf(Pets.class));
+        assertNotNull(((Pets) animals).pet1, "Empty 'pet1' property");
+        assertEquals("kitty", ((Cat) ((Pets) animals).pet1).name, "First pet has invalid name");
+        assertNotNull(((Pets) animals).pet2, "Empty 'pet2' property");
+        assertThat("Invalid pet nr 2", ((Pets) animals).pet2, instanceOf(Dog.class));
+    }
+    
+    @JsonbTypeInfo(key = "@type", value = {
+            @JsonbSubtype(alias = "Dog", type = Dog.class),
+            @JsonbSubtype(alias = "Cat", type = Cat.class)
+    })
+    public interface Pet {
+        public String getType();
+    }
+
+    public static class Dog implements Pet {
+
+        public String name;
+
+        @Override
+        public String getType() {
+            return "Dog";
+        }
+    }
+
+    public static class Cat implements Pet {
+
+        public String name;
+
+        @Override
+        public String getType() {
+            return "Cat";
+        }
+    }
+
+    @JsonbTypeInfo(key = "@type", value = {
+            @JsonbSubtype(alias = "Pets", type = Pets.class),
+            @JsonbSubtype(alias = "Fishes", type = Fishes.class)
+        })
+    public interface Animals {
+
+    }
+
+    public static class Pets implements Animals {
+        public Pet pet1;
+        public Pet pet2;
+    }
+
+    public static class Fishes implements Animals {
+
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/JsonbTransientTest.java b/src/test/java/org/eclipse/yasson/customization/transients/JsonbTransientTest.java
new file mode 100644
index 0000000..d6f0907
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/JsonbTransientTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.customization.transients.models.*;
+import jakarta.json.bind.JsonbException;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbTransientTest {
+
+    @Test
+    public void testJsonbTransientPropertySerialize() {
+        JsonbTransientValue pojo = new JsonbTransientValue();
+        pojo.setPlainProperty("non transient");
+        pojo.setPropertyTransient("TRANSIENT");
+        pojo.setGetterTransient("Getter transient value");
+        pojo.setSetterTransient("Setter transient value");
+        pojo.setGetterAndPropertyTransient("Getter and property transient value");
+        pojo.setSetterAndPropertyTransient("Setter and property transient value");
+        pojo.setSetterAndGetterTransient("Setter and getter transient value");
+        pojo.setSetterAndGetterAndPropertyTransient("Setter and getter and property transient value");
+
+        assertEquals("{\"plainProperty\":\"non transient\",\"setterTransient\":\"Setter transient value\"}", defaultJsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testJsonbTransientPropertyDeserialize() {
+        JsonbTransientValue result = defaultJsonb.fromJson("{\"plainProperty\":\"plainProperty value\"," +
+                        "\"propertyTransient\":\"TRANSIENT\"," +
+                        "\"getterTransient\":\"Getter transient value\"," +
+                        "\"setterTransient\":\"Setter transient value\"," +
+                        "\"getterAndPropertyTransient\":\"Getter and property transient value\"," +
+                        "\"setterAndPropertyTransient\":\"Setter and property transient value\"," +
+                        "\"setterAndGetterTransient\":\"Setter and getter transient value\"," +
+                        "\"setterAndGetterAndPropertyTransient\":\"Setter and getter and property transient value\"" +
+                        "}"
+                , JsonbTransientValue.class);
+
+        assertEquals("plainProperty value", result.getPlainProperty());
+        assertNull(result.getPropertyTransient());
+        assertEquals("Getter transient value", result.getGetterTransient());
+        assertNull(result.getSetterTransient());
+        assertNull(result.getGetterAndPropertyTransient());
+        assertNull(result.getSetterAndPropertyTransient());
+        assertNull(result.getSetterAndGetterTransient());
+        assertNull(result.getSetterAndGetterAndPropertyTransient());
+    }
+
+    @Test
+    public void testTransientCollidesOnProperty() throws Exception {
+        JsonbTransientCollisionOnProperty pojo = new JsonbTransientCollisionOnProperty();
+        pojo.setTransientProperty("TRANSIENT");
+
+        try {
+        	defaultJsonb.toJson(pojo);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property."));
+        }
+    }
+
+    @Test
+    public void testTransientCollidesOnGetter() throws Exception {
+        JsonbTransientCollisionOnGetter pojo = new JsonbTransientCollisionOnGetter();
+        pojo.setTransientProperty("TRANSIENT");
+
+        try {
+        	defaultJsonb.toJson(pojo);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property."));
+        }
+    }
+
+    @Test
+    public void testTransientCollidesOnPropertyAndGetter() throws Exception {
+        JsonbTransientCollisionOnPropertyAndGetter pojo = new JsonbTransientCollisionOnPropertyAndGetter();
+        pojo.setTransientProperty("TRANSIENT");
+
+        try {
+        	defaultJsonb.toJson(pojo);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property."));
+        }
+    }
+
+    @Test
+    public void testTransientCollidesOnSetter() throws Exception {
+        JsonbTransientCollisionOnSetter pojo = new JsonbTransientCollisionOnSetter();
+        pojo.setTransientProperty("TRANSIENT");
+
+        try {
+        	defaultJsonb.toJson(pojo);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property."));
+        }
+    }
+
+    @Test
+    public void testTransientCollidesOnPropertyAndSetter() throws Exception {
+        JsonbTransientCollisionOnPropertyAndSetter pojo = new JsonbTransientCollisionOnPropertyAndSetter();
+        pojo.setTransientProperty("TRANSIENT");
+
+        try {
+        	defaultJsonb.toJson(pojo);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property."));
+        }
+    }
+
+    @Test
+    public void testTransientCollidesOnPropertyAndGetterAndSetter() throws Exception {
+        JsonbTransientCollisionOnPropertyAndGetterAndSetter pojo = new JsonbTransientCollisionOnPropertyAndGetterAndSetter();
+        pojo.setTransientProperty("TRANSIENT");
+
+        try {
+        	defaultJsonb.toJson(pojo);
+            fail();
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().startsWith("JsonbTransient annotation cannot be used with other jsonb annotations on the same property."));
+        }
+    }
+
+    @Test
+    public void testTransientGetterPlusJsonbPropertyField() {
+    	assertThrows(JsonbException.class, () -> {
+	        TransientGetterPlusCustomizationAnnotatedFieldContainer pojo = new TransientGetterPlusCustomizationAnnotatedFieldContainer();
+	        defaultJsonb.toJson(pojo);
+    	});
+    }
+
+    @Test
+    public void testTransientSetterPlusJsonbPropertyField() {
+    	assertThrows(JsonbException.class, () -> {
+	        TransientSetterPlusCustomizationAnnotatedFieldContainer pojo = new TransientSetterPlusCustomizationAnnotatedFieldContainer();
+	        defaultJsonb.toJson(pojo);
+    	});
+    }
+
+    @Test
+    public void testTransientSetterplusJsonbPropertyGetter() {
+        TransientSetterPlusCustomizationAnnotatedGetterContainer pojo = new TransientSetterPlusCustomizationAnnotatedGetterContainer();
+        assertEquals("{\"instance\":\"INSTANCE\"}", defaultJsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testTransientGetterNoField() {
+        TransientGetterNoField pojo = new TransientGetterNoField();
+        assertEquals("{}", defaultJsonb.toJson(pojo));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnGetter.java b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnGetter.java
new file mode 100644
index 0000000..8cd8605
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnGetter.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbTransientCollisionOnGetter {
+
+    private String transientProperty;
+
+    @JsonbTransient
+    @JsonbProperty("custom_name")
+    public String getTransientProperty() {
+        return transientProperty;
+    }
+
+    public void setTransientProperty(String transientProperty) {
+        this.transientProperty = transientProperty;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnProperty.java b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnProperty.java
new file mode 100644
index 0000000..0fce893
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnProperty.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+/**
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class JsonbTransientCollisionOnProperty {
+
+    @JsonbTransient
+    @JsonbProperty("custom_name")
+    private String transientProperty;
+
+    public String getTransientProperty() {
+        return transientProperty;
+    }
+
+    public void setTransientProperty(String transientProperty) {
+        this.transientProperty = transientProperty;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndGetter.java b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndGetter.java
new file mode 100644
index 0000000..fdcd8e8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndGetter.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+/**
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class JsonbTransientCollisionOnPropertyAndGetter {
+
+    @JsonbTransient
+    private String transientProperty;
+
+    @JsonbProperty("custom_name")
+    public String getTransientProperty() {
+        return transientProperty;
+    }
+
+    public void setTransientProperty(String transientProperty) {
+        this.transientProperty = transientProperty;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndGetterAndSetter.java b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndGetterAndSetter.java
new file mode 100644
index 0000000..166f0a1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndGetterAndSetter.java
@@ -0,0 +1,35 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+/**
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class JsonbTransientCollisionOnPropertyAndGetterAndSetter {
+
+    @JsonbTransient
+    private String transientProperty;
+
+    @JsonbProperty("custom_name")
+    public String getTransientProperty() {
+        return transientProperty;
+    }
+
+    @JsonbProperty("custom_name")
+    public void setTransientProperty(String transientProperty) {
+        this.transientProperty = transientProperty;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndSetter.java b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndSetter.java
new file mode 100644
index 0000000..9800a77
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnPropertyAndSetter.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+/**
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class JsonbTransientCollisionOnPropertyAndSetter {
+
+    @JsonbTransient
+    private String transientProperty;
+
+    public String getTransientProperty() {
+        return transientProperty;
+    }
+
+    @JsonbProperty("custom_name")
+    public void setTransientProperty(String transientProperty) {
+        this.transientProperty = transientProperty;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnSetter.java b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnSetter.java
new file mode 100644
index 0000000..6ba9e50
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientCollisionOnSetter.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+/**
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class JsonbTransientCollisionOnSetter {
+
+    private String transientProperty;
+
+    public String getTransientProperty() {
+        return transientProperty;
+    }
+
+    @JsonbTransient
+    @JsonbProperty("custom_name")
+    public void setTransientProperty(String transientProperty) {
+        this.transientProperty = transientProperty;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientValue.java b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientValue.java
new file mode 100644
index 0000000..bdcce66
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/JsonbTransientValue.java
@@ -0,0 +1,139 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbTransient;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbTransientValue {
+
+    private String plainProperty;
+
+    @JsonbTransient
+    public String publicTransient = "publicTransient";
+
+    @JsonbTransient
+    private String transientFieldWithGetterNoSetter = "transientFieldWithGetterNoSetter";
+
+    @JsonbTransient
+    private String propertyTransient;
+
+    private String getterTransient;
+
+    private String setterTransient;
+
+    @JsonbTransient
+    private String getterAndPropertyTransient;
+
+    @JsonbTransient
+    private String setterAndPropertyTransient;
+
+    private String setterAndGetterTransient;
+
+    @JsonbTransient
+    private String setterAndGetterAndPropertyTransient;
+
+
+    public String getPropertyTransient() {
+        return propertyTransient;
+    }
+
+    public void setPropertyTransient(String propertyTransient) {
+        this.propertyTransient = propertyTransient;
+    }
+
+
+
+    public String getPlainProperty() {
+        return plainProperty;
+    }
+
+    public void setPlainProperty(String plainProperty) {
+        this.plainProperty = plainProperty;
+    }
+
+
+
+    @JsonbTransient
+    public String getGetterTransient() {
+        return getterTransient;
+    }
+
+    public void setGetterTransient(String getterTransient) {
+        this.getterTransient = getterTransient;
+    }
+
+
+
+    public String getSetterTransient() {
+        return setterTransient;
+    }
+
+    @JsonbTransient
+    public void setSetterTransient(String setterTransient) {
+        this.setterTransient = setterTransient;
+    }
+
+
+
+    @JsonbTransient
+    public String getGetterAndPropertyTransient() {
+        return getterAndPropertyTransient;
+    }
+
+    public void setGetterAndPropertyTransient(String getterAndPropertyTransient) {
+        this.getterAndPropertyTransient = getterAndPropertyTransient;
+    }
+
+
+
+    public String getSetterAndPropertyTransient() {
+        return setterAndPropertyTransient;
+    }
+
+    @JsonbTransient
+    public void setSetterAndPropertyTransient(String setterAndPropertyTransient) {
+        this.setterAndPropertyTransient = setterAndPropertyTransient;
+    }
+
+
+
+    @JsonbTransient
+    public String getSetterAndGetterTransient() {
+        return setterAndGetterTransient;
+    }
+
+    @JsonbTransient
+    public void setSetterAndGetterTransient(String setterAndGetterTransient) {
+        this.setterAndGetterTransient = setterAndGetterTransient;
+    }
+
+
+
+    @JsonbTransient
+    public String getSetterAndGetterAndPropertyTransient() {
+        return setterAndGetterAndPropertyTransient;
+    }
+
+    @JsonbTransient
+    public void setSetterAndGetterAndPropertyTransient(String setterAndGetterAndPropertyTransient) {
+        this.setterAndGetterAndPropertyTransient = setterAndGetterAndPropertyTransient;
+    }
+
+
+    public String getTransientFieldWithGetterNoSetter() {
+        return transientFieldWithGetterNoSetter;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/TransientGetterNoField.java b/src/test/java/org/eclipse/yasson/customization/transients/models/TransientGetterNoField.java
new file mode 100644
index 0000000..dad914a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/TransientGetterNoField.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbTransient;
+
+public class TransientGetterNoField {
+
+    @JsonbTransient
+    public String getFooString() {
+        return "";
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/TransientGetterPlusCustomizationAnnotatedFieldContainer.java b/src/test/java/org/eclipse/yasson/customization/transients/models/TransientGetterPlusCustomizationAnnotatedFieldContainer.java
new file mode 100644
index 0000000..8a8e706
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/TransientGetterPlusCustomizationAnnotatedFieldContainer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+public class TransientGetterPlusCustomizationAnnotatedFieldContainer {
+    @JsonbProperty("instance")
+    private String instance;
+
+    @JsonbTransient
+    public String getInstance() {
+        return instance;
+    }
+
+    public void setInstance(String instance) {
+        this.instance = instance;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/TransientSetterPlusCustomizationAnnotatedFieldContainer.java b/src/test/java/org/eclipse/yasson/customization/transients/models/TransientSetterPlusCustomizationAnnotatedFieldContainer.java
new file mode 100644
index 0000000..d80a789
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/TransientSetterPlusCustomizationAnnotatedFieldContainer.java
@@ -0,0 +1,35 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/*
+ * $Id$
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+public class TransientSetterPlusCustomizationAnnotatedFieldContainer {
+	@JsonbProperty("instance")
+    private String instance;
+
+    public String getInstance() {
+        return instance;
+    }
+
+    @JsonbTransient
+    public void setInstance(String instance) {
+        this.instance = instance;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/customization/transients/models/TransientSetterPlusCustomizationAnnotatedGetterContainer.java b/src/test/java/org/eclipse/yasson/customization/transients/models/TransientSetterPlusCustomizationAnnotatedGetterContainer.java
new file mode 100644
index 0000000..4a25355
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/customization/transients/models/TransientSetterPlusCustomizationAnnotatedGetterContainer.java
@@ -0,0 +1,35 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/*
+ * $Id$
+ */
+
+package org.eclipse.yasson.customization.transients.models;
+
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+
+public class TransientSetterPlusCustomizationAnnotatedGetterContainer {
+    private String instance = "INSTANCE";
+
+    @JsonbProperty("instance")
+    public String getInstance() {
+        return instance;
+    }
+
+    @JsonbTransient
+    public void setInstance(String instance) {
+        this.instance = instance;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/EnumTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/EnumTest.java
new file mode 100644
index 0000000..3d81702
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/EnumTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.collections.Language;
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+
+public class EnumTest {
+
+    @Test
+    public void testEnumValue() {
+        assertEquals("\"Russian\"", defaultJsonb.toJson(Language.Russian));
+        Language result = defaultJsonb.fromJson("\"Russian\"", Language.class);
+        assertEquals(Language.Russian, result);
+    }
+
+    @Test
+    public void testEnumInObject() {
+        assertEquals("{\"value\":\"Russian\"}", defaultJsonb.toJson(new ScalarValueWrapper<>(Language.Russian)));
+        ScalarValueWrapper<Language> result = defaultJsonb.fromJson("{\"value\":\"Russian\"}", new TestTypeToken<ScalarValueWrapper<Language>>() {
+        }.getType());
+
+        assertEquals(result.getValue(), Language.Russian);
+    }
+
+    @Test
+    public void testEnumValueWithToStringOverriden() {
+        assertEquals("\"HARD_BACK\"", defaultJsonb.toJson(Binding.HARD_BACK));
+        Binding result = defaultJsonb.fromJson("\"HARD_BACK\"", Binding.class);
+        assertEquals(Binding.HARD_BACK, result);
+    }
+
+    @Test
+    public void testEnumInObjectWithToStringOverriden() {
+        assertEquals("{\"value\":\"HARD_BACK\"}", defaultJsonb.toJson(new ScalarValueWrapper<>(Binding.HARD_BACK)));
+        ScalarValueWrapper<Binding> result = defaultJsonb.fromJson("{\"value\":\"HARD_BACK\"}", new TestTypeToken<ScalarValueWrapper<Binding>>(){}.getType());
+        assertEquals(Binding.HARD_BACK, result.getValue());
+    }
+
+    public enum Binding {
+        HARD_BACK {
+            @Override
+			public String toString() {
+                return "Hard Back";
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/IJsonTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/IJsonTest.java
new file mode 100644
index 0000000..de93f7b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/IJsonTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import java.time.*;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class IJsonTest {
+
+    private Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withStrictIJSON(true));
+
+    @Test
+    public void testStrictCalendar() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.clear();
+        calendar.set(1970, 0, 1, 0, 0, 0);
+        calendar.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
+
+        String jsonString = jsonb.toJson(new ScalarValueWrapper<>(calendar));
+        assertEquals("{\"value\":\"1970-01-01T00:00:00Z+01:00\"}", jsonString);
+
+        ScalarValueWrapper<Calendar> result = jsonb.fromJson("{\"value\":\"1970-01-01T00:00:00Z+01:00\"}", new TestTypeToken<ScalarValueWrapper<Calendar>>() {}.getType());
+
+        assertEquals(calendar.toInstant(), result.getValue().toInstant());
+    }
+
+    @Test
+    public void testStrictDate() {
+        Calendar calendar = Calendar.getInstance();
+        calendar.set(1970, 0, 1, 0, 0, 0);
+        calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
+        calendar.clear(Calendar.MILLISECOND);
+
+        String jsonString = jsonb.toJson(new ScalarValueWrapper<>(calendar.getTime()));
+        assertTrue(jsonString.matches("\\{\"value\":\"1970-01-01T00:00:00Z\\+[0-9]{2}:[0-9]{2}\"}"));
+
+        ScalarValueWrapper<Date> result = jsonb.fromJson("{\"value\":\"1970-01-01T00:00:00Z+00:00\"}", new TestTypeToken<ScalarValueWrapper<Date>>(){}.getType());
+        assertEquals(0, result.getValue().compareTo(calendar.getTime()));
+
+    }
+
+    @Test
+    public void testStrictInstant() {
+        Instant instant = LocalDateTime.of(2017, 3, 24, 12,0,0).toInstant(ZoneOffset.MIN);
+        final String json = jsonb.toJson(new ScalarValueWrapper<>(instant));
+        assertEquals("{\"value\":\"2017-03-25T06:00:00Z+00:00\"}", json);
+        ScalarValueWrapper<Instant> result = jsonb.fromJson("{\"value\":\"2017-03-25T06:00:00Z+00:00\"}", new TestTypeToken<ScalarValueWrapper<Instant>>() {}.getType());
+        assertEquals(instant, result.getValue());
+    }
+
+    @Test
+    public void testLocalDate() {
+        final LocalDate localDate = LocalDate.of(1970, 1, 1);
+        final String json = jsonb.toJson(new ScalarValueWrapper<>(localDate));
+        assertEquals("{\"value\":\"1970-01-01T00:00:00Z+00:00\"}", json);
+
+        ScalarValueWrapper<LocalDate> result = jsonb.fromJson("{\"value\":\"1970-01-01T00:00:00Z+00:00\"}", new TestTypeToken<ScalarValueWrapper<LocalDate>>() {
+        }.getType());
+
+
+        assertEquals(localDate, result.getValue());
+    }
+
+    @Test
+    public void testLocalDateTime() {
+        final LocalDateTime localDateTime = LocalDateTime.of(1970, 1, 1, 1, 1, 1);
+        final String json = jsonb.toJson(new ScalarValueWrapper<>(localDateTime));
+
+        assertEquals("{\"value\":\"1970-01-01T01:01:01Z+00:00\"}", json);
+
+        ScalarValueWrapper<LocalDateTime> result = jsonb.fromJson("{\"value\":\"1970-01-01T01:01:01Z+00:00\"}", new TestTypeToken<ScalarValueWrapper<LocalDateTime>>() {
+        }.getType());
+
+
+        assertEquals(localDateTime, result.getValue());
+    }
+
+    @Test
+    public void testDuration() {
+        Duration duration = Duration.ofDays(1).plus(Duration.ofHours(1)).plus(Duration.ofSeconds(1));
+
+        final String json = jsonb.toJson(new ScalarValueWrapper<>(duration));
+        assertEquals("{\"value\":\"PT25H1S\"}", json);
+
+        ScalarValueWrapper<Duration> result = jsonb.fromJson(json, new TestTypeToken<ScalarValueWrapper<Duration>>() {
+        }.getType());
+        assertEquals(duration, result.getValue());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/anonymous/AnonymousClassTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/anonymous/AnonymousClassTest.java
new file mode 100644
index 0000000..e94c1ed
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/anonymous/AnonymousClassTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.anonymous;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+/**
+ * This class contains tests for marshalling/unmarshalling anonymous classes.
+ *
+ * @author Dmitry Kornilov
+ */
+public class AnonymousClassTest {
+
+    @Test
+    public void testMarshallInnerClass() {
+        assertEquals("{\"anonymousField\":\"anonymousValue\",\"id\":1,\"name\":\"pojoName\"}", defaultJsonb.toJson(new InnerPojo() {
+            public String anonymousField = "anonymousValue";
+
+            @Override
+            public Integer getId() {
+                return 1;
+            }
+
+            @Override
+            public String getName() {
+                return "pojoName";
+            }
+        }));
+    }
+
+    @Test
+    public void testMarshallOuterClass() {
+        assertEquals("{\"id\":1,\"anonymousField\":\"anonymousValue\"}", defaultJsonb.toJson(new OuterPojo() {
+            public String anonymousField = "anonymousValue";
+        }));
+    }
+
+    public static class InnerPojo {
+        private Integer id;
+        private String name;
+
+        public Integer getId() {
+            return id;
+        }
+
+        public void setId(Integer id) {
+            this.id = id;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/anonymous/OuterPojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/anonymous/OuterPojo.java
new file mode 100644
index 0000000..544e473
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/anonymous/OuterPojo.java
@@ -0,0 +1,39 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.anonymous;
+
+/**
+ * Test class.
+ *
+ * @author Dmitry Kornilov
+ */
+public class OuterPojo {
+    private Integer id = 1;
+    private String name;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/BasicTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/BasicTest.java
new file mode 100644
index 0000000..0de0a4b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/BasicTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.basic;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Default mapping primitives tests.
+ *
+ * @author Dmitry Kornilov
+ */
+public class BasicTest {
+
+    @Test
+    public void testMarshallEscapedString() {
+        assertEquals("[\" \\\\ \\\" / \\f\\b\\r\\n\\t 9\"]", bindingJsonb.toJson(new String[] {" \\ \" / \f\b\r\n\t \u0039"}));
+    }
+
+    @Test
+    public void testMarshallWriter() {
+        Writer writer = new StringWriter();
+        bindingJsonb.toJson(new Long[]{5L}, writer);
+        assertEquals("[5]", writer.toString());
+    }
+    
+    @Test
+    public void testDoubleWriter() throws Exception {
+        Writer writer = new StringWriter();
+        writer.write("{");
+        bindingJsonb.toJson("Hello", writer);
+        writer.write(",");
+        bindingJsonb.toJson("World", writer);
+        writer.write("}");
+        assertEquals("{\"Hello\",\"World\"}", writer.toString());
+    }
+
+    @Test
+    public void testMarshallOutputStream() throws IOException {
+        try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+        	bindingJsonb.toJson(new Long[]{5L}, baos);
+            assertEquals("[5]", baos.toString("UTF-8"));
+        }
+    }
+
+    @Test
+    public void testObjectSerialization() {
+        final String val = bindingJsonb.toJson(new Object());
+        assertEquals("{}", val);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/BooleanTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/BooleanTest.java
new file mode 100644
index 0000000..c89d32f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/BooleanTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.basic;
+
+import org.junit.jupiter.api.*;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.defaultmapping.basic.model.BooleanModel;
+
+/**
+ * Tests serialization and deserialization of boolean values.
+ *
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class BooleanTest {
+
+    @Test
+    public void testBooleanSerialization() throws Exception {
+        BooleanModel booleanModel = new BooleanModel(true, false);
+
+        String expected = "{\"field1\":true,\"field2\":false}";
+        assertEquals(expected, defaultJsonb.toJson(booleanModel));
+    }
+
+    @Test
+    public void testBooleanDeserializationFromBooleanAsStringValue() throws Exception {
+        BooleanModel booleanModel = defaultJsonb.fromJson("{\"field1\":\"true\",\"field2\":\"true\"}", BooleanModel.class);
+        assertEquals(true, booleanModel.field1);
+        assertEquals(true, booleanModel.field2);
+    }
+
+    @Test
+    public void testBooleanDeserializationFromBooleanRawValue() throws Exception {
+        BooleanModel booleanModel = defaultJsonb.fromJson("{\"field1\":false,\"field2\":false}", BooleanModel.class);
+        assertThat(booleanModel.field1, is(false));
+        assertThat(booleanModel.field2, is(false));
+    }
+
+    @Test
+    public void testNakedBooleans() {
+        assertEquals(true, defaultJsonb.fromJson("true", boolean.class));
+        assertEquals(true, defaultJsonb.fromJson("true", Boolean.class));
+        assertEquals(false, defaultJsonb.fromJson("false", boolean.class));
+        assertEquals(false, defaultJsonb.fromJson("false", Boolean.class));
+        
+        assertEquals("true", defaultJsonb.toJson(true, boolean.class));
+        assertEquals("false", defaultJsonb.toJson(Boolean.FALSE, Boolean.class));
+    }
+    
+    //Fix for issue #390
+    @Test
+    public void testBooleanArrays() {
+        assertArrayEquals(new boolean[] {true, false}, defaultJsonb.fromJson("[true, false]", boolean[].class));
+        assertArrayEquals(new Boolean[] {true, false}, defaultJsonb.fromJson("[true, false]", Boolean[].class));
+        
+        assertEquals("[true,false]", defaultJsonb.toJson(new boolean[] {true, false}));
+        assertEquals("[true,false]", defaultJsonb.toJson(new Boolean[] {true, false}));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
new file mode 100644
index 0000000..6488884
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
@@ -0,0 +1,242 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.basic;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import static org.eclipse.yasson.Assertions.*;
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.basic.model.BigDecimalInNumber;
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.JsonWriter;
+import jakarta.json.stream.JsonGenerator;
+
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class NumberTest {
+
+    @Test
+    public void testSerializeFloat() {
+        final String json = defaultJsonb.toJson(0.35f);
+        assertEquals("0.35", json);
+
+        Float result = defaultJsonb.fromJson("0.35", Float.class);
+        assertEquals((Float) .35f, result);
+    }
+
+    @Test
+    public void testBigDecimalMarshalling() {
+        String jsonString = defaultJsonb.toJson(new BigDecimal("0.10000000000000001"));
+        assertEquals("0.10000000000000001", jsonString);
+
+        jsonString = defaultJsonb.toJson(new BigDecimal("0.1000000000000001"));
+        assertEquals("0.1000000000000001", jsonString);
+
+        BigDecimal result = defaultJsonb.fromJson("0.10000000000000001", BigDecimal.class);
+        assertEquals(new BigDecimal("0.10000000000000001"), result);
+
+        result = defaultJsonb.fromJson("0.100000000000000001", BigDecimal.class);
+        assertEquals(new BigDecimal("0.100000000000000001"), result);
+    }
+
+    @Test
+    public void testBigDecimalIEEE748() {
+        String jsonString = defaultJsonb.toJson(new BigDecimal("9007199254740991"));
+        assertEquals("9007199254740991", jsonString);
+
+        jsonString = defaultJsonb.toJson(new BigDecimal("9007199254740992"));
+        assertEquals("9007199254740992", jsonString);
+
+        jsonString = defaultJsonb.toJson(new BigDecimal("9007199254740991.1"));
+        assertEquals("9007199254740991.1", jsonString);
+
+        jsonString = defaultJsonb.toJson(new BigDecimal(new BigInteger("1"), -400));
+        assertEquals(new BigDecimal(new BigInteger("1"), -400).toString(), jsonString);
+    }
+
+    @Test
+    public void testBigIntegerIEEE748() {
+        String jsonString = defaultJsonb.toJson(new BigInteger("9007199254740991"));
+        assertEquals("9007199254740991", jsonString);
+
+        jsonString = defaultJsonb.toJson(new BigInteger("9007199254740992"));
+        assertEquals("9007199254740992", jsonString);
+    }
+
+    @Test
+    public void testBigDecimalInNumber() {
+        BigDecimalInNumber testValueQuoted = new BigDecimalInNumber() {{setBigDecValue(new BigDecimal("9007199254740992"));}};
+        BigDecimalInNumber testValueUnQuoted = new BigDecimalInNumber() {{setBigDecValue(new BigDecimal("9007199254740991"));}};
+        String jsonString = defaultJsonb.toJson(testValueQuoted);
+        assertEquals("{\"bigDecValue\":9007199254740992}", jsonString);
+
+        jsonString = defaultJsonb.toJson(testValueUnQuoted);
+        assertEquals("{\"bigDecValue\":9007199254740991}", jsonString);
+
+        BigDecimalInNumber result = defaultJsonb.fromJson("{\"bigDecValue\":9007199254740992}", BigDecimalInNumber.class);
+        assertEquals(testValueQuoted.getBigDecValue(), result.getBigDecValue());
+
+        result = defaultJsonb.fromJson("{\"bigDecValue\":9007199254740991}", BigDecimalInNumber.class);
+        assertEquals(testValueUnQuoted.getBigDecValue(), result.getBigDecValue());
+    }
+
+    @Test
+    public void testBigDecimalWrappedMarshalling() {
+        String jsonString = defaultJsonb.toJson(new ScalarValueWrapper<>(new BigDecimal("0.1000000000000001")));
+        assertEquals("{\"value\":0.1000000000000001}", jsonString);
+
+        jsonString = defaultJsonb.toJson(new ScalarValueWrapper<>(new BigDecimal("0.10000000000000001")));
+        assertEquals("{\"value\":0.10000000000000001}", jsonString);
+
+        ScalarValueWrapper<BigDecimal> result = defaultJsonb.fromJson("{\"value\":0.1000000000000001}", new TestTypeToken<ScalarValueWrapper<BigDecimal>>(){}.getType());
+        assertEquals(new BigDecimal("0.1000000000000001"), result.getValue());
+
+        result = defaultJsonb.fromJson("{\"value\":0.10000000000000001}", new TestTypeToken<ScalarValueWrapper<BigDecimal>>(){}.getType());
+        assertEquals(new BigDecimal("0.10000000000000001"), result.getValue());
+    }
+
+    @Test
+    public void testBigDecimalCastedToNumber() {
+        String jsonString = defaultJsonb.toJson(new Object() { public Number number = new BigDecimal("0.10000000000000001"); });
+        assertEquals("{\"number\":0.10000000000000001}", jsonString);
+
+        jsonString = defaultJsonb.toJson(new Object() { public Number number = new BigDecimal("0.1000000000000001"); });
+        assertEquals("{\"number\":0.1000000000000001}", jsonString);
+    }
+
+    @Test
+    public void testLongIEEE748() {
+        // 9007199254740991L
+        Long maxJsSafeValue = Double.valueOf(Math.pow(2, 53)).longValue() - 1;
+        Long upperJsUnsafeValue = maxJsSafeValue + 1;
+
+        String json = defaultJsonb.toJson(maxJsSafeValue);
+        assertEquals("9007199254740991", json);
+        Long deserialized = defaultJsonb.fromJson(json, Long.class);
+        assertEquals(Long.valueOf("9007199254740991"), deserialized);
+
+        json = defaultJsonb.toJson(upperJsUnsafeValue);
+        assertEquals("9007199254740992", json);
+        deserialized = defaultJsonb.fromJson(json, Long.class);
+        assertEquals(Long.valueOf("9007199254740992"), deserialized);
+
+
+        Long minJsSafeValue = Math.negateExact(maxJsSafeValue);
+        Long lowerJsUnsafeValue = minJsSafeValue - 1;
+
+        json = defaultJsonb.toJson(minJsSafeValue);
+        assertEquals("-9007199254740991", json);
+        deserialized = defaultJsonb.fromJson(json, Long.class);
+        assertEquals(Long.valueOf("-9007199254740991"), deserialized);
+
+        json = defaultJsonb.toJson(lowerJsUnsafeValue);
+        assertEquals("-9007199254740992", json);
+        deserialized = defaultJsonb.fromJson(json, Long.class);
+        assertEquals(Long.valueOf("-9007199254740992"), deserialized);
+    }
+
+    /**
+     * Tests that JSON-P RI itself does no big number (out of IEEE 754 quotation).
+     * This is why it is now must be done in Yasson to match the JSONB spec.
+     */
+    @Test
+    public void testJsonpBigNumber() {
+        StringWriter w = new StringWriter();
+        JsonGenerator generator = Json.createGenerator(w);
+
+        Long maxJsSafeValue = Double.valueOf(Math.pow(2, 53)).longValue() - 1;
+        Long upperJsUnsafeValue = Long.MAX_VALUE;
+
+        generator.writeStartObject();
+        generator.write("safeLongValue", maxJsSafeValue);
+        generator.write("unsafeLongValue", upperJsUnsafeValue);
+        generator.write("safeBigDecimalValue", BigDecimal.TEN);
+        generator.write("unsafeBigDecimalValue", BigDecimal.valueOf(upperJsUnsafeValue));
+        generator.writeEnd();
+        generator.close();
+
+        assertEquals("{" +
+                        "\"safeLongValue\":9007199254740991," +
+                        "\"unsafeLongValue\":9223372036854775807," +
+                        "\"safeBigDecimalValue\":10," +
+                        "\"unsafeBigDecimalValue\":9223372036854775807}",
+                w.toString());
+
+
+        w = new StringWriter();
+        JsonWriter writer = Json.createWriter(w);
+
+
+        JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+        objectBuilder.add("safeLongValue", maxJsSafeValue);
+        objectBuilder.add("unsafeLongValue", upperJsUnsafeValue);
+        objectBuilder.add("safeBigDecimalValue", BigDecimal.valueOf(maxJsSafeValue));
+        objectBuilder.add("unsafeBigDecimalValue", BigDecimal.valueOf(upperJsUnsafeValue));
+        JsonObject build = objectBuilder.build();
+        writer.write(build);
+        writer.close();
+
+        assertEquals("{" +
+                "\"safeLongValue\":9007199254740991," +
+                "\"unsafeLongValue\":9223372036854775807," +
+                "\"safeBigDecimalValue\":9007199254740991," +
+                "\"unsafeBigDecimalValue\":9223372036854775807}", w.toString());
+
+    }
+    
+    public static class NumberContainer {
+    	public Double doubleProp;
+    	public Collection<Double> collectionProp;
+    	public Map<String,Double> mapProp;
+    }
+    
+    @Test
+    public void testSerializeInvalidDouble() {
+        shouldFail(() -> defaultJsonb.toJson(Double.POSITIVE_INFINITY));
+
+        NumberContainer obj = new NumberContainer();
+        obj.doubleProp = Double.POSITIVE_INFINITY;
+        shouldFail(() -> defaultJsonb.toJson(obj), msg -> msg.contains("doubleProp") && msg.contains("NumberContainer"));
+    }
+    
+    
+    @Test
+    public void testSerializeInvalidDoubleCollection() {
+        NumberContainer obj = new NumberContainer();
+        obj.collectionProp = Collections.singleton(Double.POSITIVE_INFINITY);
+        shouldFail(() -> defaultJsonb.toJson(obj),
+                  msg -> msg.contains("collectionProp") && msg.contains("NumberContainer"));
+    }
+
+    @Test
+    public void testSerializeInvalidDoubleMap() {
+        NumberContainer obj = new NumberContainer();
+        obj.mapProp = Collections.singletonMap("doubleKey", Double.POSITIVE_INFINITY);
+        shouldFail(() -> defaultJsonb.toJson(obj),
+                  msg -> msg.contains("mapProp") && msg.contains("NumberContainer"));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/PropertyMismatchTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/PropertyMismatchTest.java
new file mode 100644
index 0000000..505e962
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/PropertyMismatchTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2016, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.basic;
+
+import java.net.URI;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Objects;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+import org.jboss.weld.exceptions.IllegalStateException;
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Tests to verify that read-only properties (properties with no field or setter)
+ * are ignored when deserializing and that write-only properties (properties with
+ * no field or getter) are ignored when serializing.
+ */
+public class PropertyMismatchTest {
+
+    public static class CollectionGetterOnly {
+        public HiddenCtorCollection getDataCollection() {
+            throw new IllegalStateException("Get was called");
+        }
+
+        public HiddenCtorMap getDataMap() {
+            throw new IllegalStateException("Get was called");
+        }
+
+        public HiddenCtorType[] getDataArray() {
+            throw new IllegalStateException("Get was called");
+        }
+
+        public HiddenCtorType getData() {
+            throw new IllegalStateException("Get was called");
+        }
+    }
+
+    public static class CollectionSetterOnly {
+        public void setDataCollection(HiddenCtorCollection set) {
+            throw new IllegalStateException("Set was called");
+        }
+
+        public void setDataMap(HiddenCtorMap map) {
+            throw new IllegalStateException("Set was called");
+        }
+
+        public void setDataArray(HiddenCtorType[] arr) {
+            throw new IllegalStateException("Set was called");
+        }
+
+        public void setData(HiddenCtorType obj) {
+            throw new IllegalStateException("Set was called");
+        }
+    }
+
+    public static class HiddenCtorCollection extends HashSet<String> {
+        private static final long serialVersionUID = -2254550505591024068L;
+
+        private HiddenCtorCollection() {
+            throw new IllegalStateException("Object should not be initialized!");
+        }
+    }
+
+    public static class HiddenCtorMap extends HashMap<String,String> {
+        private static final long serialVersionUID = -3042588327575185446L;
+
+        private HiddenCtorMap() {
+            throw new IllegalStateException("Object should not be initialized!");
+        }
+    }
+
+    public static class HiddenCtorType {
+
+        private HiddenCtorType() {
+            throw new IllegalStateException("Object should not be initialized!");
+        }
+    }
+
+    /**
+     * When deserializing a JSON, a property without a setter should be ignored as a property
+     */
+    @Test
+    public void testGetterOnly() {
+        String jsonCollection = "{\"dataCollection\": [\"foo\"], " +
+                                 "\"dataMap\": { \"foo\": \"bar\" }, " +
+                                 "\"dataArray\": [\"foo\"], " +
+                                 "\"data\": \"foo\" }";
+        CollectionGetterOnly collection = defaultJsonb.fromJson(jsonCollection, CollectionGetterOnly.class);
+        // Don't need to verify resulting object (except that it is non-null)
+        // because if any getters or ctors were called, we would get an ISE
+        assertNotNull(collection);
+    }
+
+    /**
+     * When serializing an object, a property without a getter should be ignored as a property
+     */
+    @Test
+    public void testSetterOnly() {
+        CollectionSetterOnly obj = new CollectionSetterOnly();
+        String json = defaultJsonb.toJson(obj);
+        assertEquals("{}", json);
+    }
+    
+    public static class PropertyTypeMismatch {
+        @JsonbTransient
+        public Instant internalInstantProperty;
+        
+        private String foo;
+        public int getFoo() {
+            return foo.length();
+        }
+        public void setFoo(Instant instant) {
+            this.foo = instant.toString();
+            this.internalInstantProperty = instant;
+        }
+    }
+
+    @Test
+    public void testTransientAndPropertyAnnotationMerge() {
+        TransientAndPropertyAnnotationMerge object = new TransientAndPropertyAnnotationMerge();
+        String expected = "{\"number\":\"http://localhost/\"}";
+        String json = defaultJsonb.toJson(object);
+        assertEquals(expected, json);
+        TransientAndPropertyAnnotationMerge deserialized = defaultJsonb.fromJson(expected,
+                                                                                 TransientAndPropertyAnnotationMerge.class);
+        assertEquals(object, deserialized);
+    }
+
+    public static class TransientAndPropertyAnnotationMerge {
+
+        @JsonbTransient
+        private Integer number;
+
+        @JsonbProperty("number")
+        private URI someLink;
+
+        public TransientAndPropertyAnnotationMerge() {
+            number = -1;
+            someLink = URI.create("http://localhost/");
+        }
+
+        public Integer getNumber() {
+            return number;
+        }
+
+        public void setNumber(Integer number) {
+            this.number = number;
+        }
+
+        public URI getSomeLink() {
+            return someLink;
+        }
+
+        public void setSomeLink(URI someLink) {
+            this.someLink = someLink;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            TransientAndPropertyAnnotationMerge that = (TransientAndPropertyAnnotationMerge) o;
+            return Objects.equals(number, that.number) && Objects.equals(someLink, that.someLink);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(number, someLink);
+        }
+    }
+
+    /**
+     * Test that properties of the same name with different
+     * field/getter/setter types behave properly and that we don't
+     * assume they are all equal
+     */
+    @Test
+    public void testPropertyTypesMismatch() {
+        PropertyTypeMismatch obj = new PropertyTypeMismatch();
+        Instant now = Instant.now();
+        obj.setFoo(now);
+        
+        String json = defaultJsonb.toJson(obj);
+        assertEquals("{\"foo\":" + now.toString().length() + "}", json);
+        
+        PropertyTypeMismatch after = defaultJsonb.fromJson("{\"foo\":\"" + now.toString() + "\"}", PropertyTypeMismatch.class);
+        assertEquals(obj.getFoo(), after.getFoo());
+        assertEquals(obj.internalInstantProperty, after.internalInstantProperty);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/SingleValueTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/SingleValueTest.java
new file mode 100644
index 0000000..a08e4e8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/SingleValueTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.basic;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.internal.JsonBindingBuilder;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * @author David Kral
+ */
+public class SingleValueTest {
+
+    @Test
+    public void testMarshallPrimitives() {
+        // String
+        assertEquals("\"some_string\"", bindingJsonb.toJson("some_string"));
+
+        // Character
+        assertEquals("\"\uFFFF\"", bindingJsonb.toJson('\uFFFF'));
+
+        // Byte
+        assertEquals("1", bindingJsonb.toJson((byte)1));
+
+        // Short
+        assertEquals("1", bindingJsonb.toJson((short)1));
+
+        // Integer
+        assertEquals("1", bindingJsonb.toJson(1));
+
+        // Long
+        assertEquals("5", bindingJsonb.toJson(5L));
+
+        // Float
+        assertEquals("1.2", bindingJsonb.toJson(1.2f));
+
+        // Double
+        assertEquals("1.2", bindingJsonb.toJson(1.2));
+
+        // BigInteger
+        assertEquals("1", bindingJsonb.toJson(new BigInteger("1")));
+
+        // BigDecimal
+        assertEquals("1.2", bindingJsonb.toJson(new BigDecimal("1.2")));
+
+        // Number
+        assertEquals("1.2", bindingJsonb.toJson(1.2));
+
+        // Boolean true
+        assertEquals("true", bindingJsonb.toJson(true));
+
+        // Boolean false
+        assertEquals("false", bindingJsonb.toJson(false));
+
+        assertEquals("1", bindingJsonb.toJson(1));
+
+        // null
+        assertEquals("null", bindingJsonb.toJson(null));
+    }
+
+    @Test
+    public void testSingleValue() {
+        assertEquals("5", bindingJsonb.toJson(5));
+
+        Jsonb jsonb = new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true)).build();
+        try {
+            jsonb.toJson(5);
+            fail();
+        } catch (JsonbException exception){
+            assertEquals(Messages.getMessage(MessageKeys.IJSON_ENABLED_SINGLE_VALUE), exception.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/UnqualifiedPropertiesTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/UnqualifiedPropertiesTest.java
new file mode 100644
index 0000000..dd77ed1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/UnqualifiedPropertiesTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.basic;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+public class UnqualifiedPropertiesTest {
+	
+	public static class Widget {
+		
+		public long now;
+
+	    public String getFoo() {
+	        return "foo";
+	    }
+
+	    public String getBar(final int baz) {
+	        return "bar" + baz;
+	    }
+	    
+	    public boolean isPositive(int num) {
+	    	return num > 0;
+	    }
+	    
+	    public Widget setNow() {
+	    	now = 1511576115722L;
+	    	return this;
+	    }
+	}
+	
+	@Test
+	public void testGetWithArgs() {
+	    assertEquals("{\"foo\":\"foo\",\"now\":0}", defaultJsonb.toJson(new Widget()));
+	}
+	
+	@Test
+	public void testSetWithNoArgs() {
+		assertEquals("{\"foo\":\"foo\",\"now\":1511576115722}", defaultJsonb.toJson(new Widget().setNow()));
+	}
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/model/BigDecimalInNumber.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/model/BigDecimalInNumber.java
new file mode 100644
index 0000000..1b722a0
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/model/BigDecimalInNumber.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 Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.basic.model;
+
+/**
+ * Encapsulates Number type of field so the serialization and deserialization of number can be tested
+ *
+ * @author David Kral
+ */
+public class BigDecimalInNumber {
+
+    private Number bigDecValue;
+
+    public BigDecimalInNumber(){
+    }
+
+    public Number getBigDecValue() {
+        return bigDecValue;
+    }
+
+    public void setBigDecValue(Number bigDecValue) {
+        this.bigDecValue = bigDecValue;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/model/BooleanModel.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/model/BooleanModel.java
new file mode 100644
index 0000000..58bc142
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/model/BooleanModel.java
@@ -0,0 +1,32 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.basic.model;
+
+/**
+ * Encapsulates different types of boolean values as a field so that the boolean value's serialization and
+ * deserialization could be tested.
+ *
+ * @author Ehsan Zaery Moghaddam (zaerymoghaddam@gmail.com)
+ */
+public class BooleanModel {
+    public Boolean field1;
+    public boolean field2;
+
+    public BooleanModel() {
+    }
+
+    public BooleanModel(boolean field1, Boolean field2) {
+        this.field2 = field2;
+        this.field1 = field1;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/collections/ArrayTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/collections/ArrayTest.java
new file mode 100644
index 0000000..786cc2d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/collections/ArrayTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.collections;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ArrayTest {
+
+    @Test
+    public void testStringArray() {
+        String[] stringArray = new String[3];
+        stringArray[0] = "first";
+        stringArray[1] = "second";
+        stringArray[2] = "third";
+
+        String expected = "[\"first\",\"second\",\"third\"]";
+
+        assertEquals(expected, nullableJsonb.toJson(stringArray));
+
+        String[] result = nullableJsonb.fromJson(expected, stringArray.getClass());
+        assertEquals("first", result[0]);
+        assertEquals("second", result[1]);
+        assertEquals("third", result[2]);
+    }
+
+    @Test
+    public void testObjectArrayOfObjects() {
+        Object[] objectArray = new Object[2];
+        objectArray[0] = new KeyValue("first");
+        objectArray[1] = new KeyValue("second");
+
+        String expected = "[{\"field\":\"first\"},{\"field\":\"second\"}]";
+
+        assertEquals(expected, nullableJsonb.toJson(objectArray));
+        Object[] result = nullableJsonb.fromJson(expected, objectArray.getClass());
+        assertEquals(HashMap.class, result[0].getClass());
+        assertEquals(HashMap.class, result[1].getClass());
+        assertEquals("first", ((Map) result[0]).get("field"));
+        assertEquals("second", ((Map) result[1]).get("field"));
+    }
+
+    @Test
+    public void testListOfArrays() {
+        List<String[]> listOfArrays = new ArrayList<>();
+        String[] stringArray = new String[2];
+        stringArray[0] = "first";
+        stringArray[1] = "second";
+        listOfArrays.add(stringArray);
+        listOfArrays.add(stringArray);
+
+        String expected = "[[\"first\",\"second\"],[\"first\",\"second\"]]";
+        assertEquals(expected, nullableJsonb.toJson(listOfArrays));
+        List<String[]> result = nullableJsonb.fromJson(expected, new TestTypeToken<ArrayList<String[]>>(){}.getType());
+        assertEquals("first", result.get(0)[0]);
+        assertEquals("second", result.get(0)[1]);
+        assertEquals("first", result.get(1)[0]);
+        assertEquals("second", result.get(1)[1]);
+    }
+
+    @Test
+    public void testMultidimensionalArrays() {
+        String[][] multi = new String[2][2];
+        multi[0][0] = "[0],[0]";
+        multi[0][1] = "[0],[1]";
+        multi[1][0] = "[1],[0]";
+        multi[1][1] = "[1],[1]";
+        String expected = "[[\"[0],[0]\",\"[0],[1]\"],[\"[1],[0]\",\"[1],[1]\"]]";
+        assertEquals(expected, nullableJsonb.toJson(multi));
+        String[][] result = nullableJsonb.fromJson(expected, multi.getClass());
+        assertEquals("[0],[0]", result[0][0]);
+        assertEquals("[0],[1]", result[0][1]);
+        assertEquals("[1],[0]", result[1][0]);
+        assertEquals("[1],[1]", result[1][1]);
+    }
+
+    @Test
+    public void testDeserializeJsonArrayIntoObject() {
+        String json = "[\"first\",\"second\",\"third\"]";
+        Object result = nullableJsonb.fromJson(json, Object.class);
+        assertTrue(result instanceof List);
+        assertEquals("first", ((List)result).get(0));
+        assertEquals("second", ((List)result).get(1));
+        assertEquals("third", ((List)result).get(2));
+    }
+
+    @Test
+    public void testDeserializeJsonObjectIntoListOfMaps() {
+        String json = "[{\"first\":1,\"second\":10}]";
+        Object result = nullableJsonb.fromJson(json, List.class);
+        assertTrue(result instanceof List);
+        assertEquals(BigDecimal.ONE, ((Map) ((List) result).get(0)).get("first"));
+        assertEquals(BigDecimal.TEN, ((Map) ((List) result).get(0)).get("second"));
+    }
+
+    @Test
+    public void testUnmarshallMapWithArrayValue() {
+        Map<String, String[]> arrayValueMap = new HashMap<>();
+        String[] strings = new String[2];
+        strings[0] = "zero";
+        strings[1] = "one";
+        arrayValueMap.put("first", strings);
+        String expected = "{\"first\":[\"zero\",\"one\"]}";
+        assertEquals(expected, nullableJsonb.toJson(arrayValueMap));
+        Map<String, String[]> result = nullableJsonb.fromJson(expected, new TestTypeToken<HashMap<String, String[]>>(){}.getType());
+        assertEquals("zero", result.get("first")[0]);
+        assertEquals("one", result.get("first")[1]);
+    }
+
+    @Test
+    public void testArrayOfNulls() {
+        String[] nulls = new String[2];
+        String expected = "[null,null]";
+        assertEquals(expected, nullableJsonb.toJson(nulls));
+        String[] result = nullableJsonb.fromJson(expected, nulls.getClass());
+        assertTrue(result.length == 2);
+        assertNull(result[0]);
+        assertNull(result[1]);
+
+        Integer ints[] = new Integer[2];
+        assertEquals(expected, nullableJsonb.toJson(ints));
+    }
+
+    @Test
+    public void testByteArray() {
+        byte[] byteArr = {-128, 127};
+        assertEquals("[-128,127]", nullableJsonb.toJson(byteArr));
+        assertArrayEquals(byteArr, nullableJsonb.fromJson("[-128, 127]", byte[].class));
+    }
+
+    @Test
+    public void testCharArray() {
+        char[] charArr = {'a', 'b', 'c'};
+        assertEquals("[\"a\",\"b\",\"c\"]", nullableJsonb.toJson(charArr));
+        assertArrayEquals(charArr, nullableJsonb.fromJson("[\"a\",\"b\",\"c\"]", char[].class));
+    }
+
+    @Test
+    public void testShortArray() {
+        short[] shortArr = {-128, 127};
+        assertEquals("[-128,127]", nullableJsonb.toJson(shortArr));
+        assertArrayEquals(shortArr, nullableJsonb.fromJson("[-128, 127]", short[].class));
+    }
+
+    @Test
+    public void testIntArray() {
+        int[] intArr = {-128, 127};
+        assertEquals("[-128,127]", nullableJsonb.toJson(intArr));
+        assertArrayEquals(intArr, nullableJsonb.fromJson("[-128, 127]", int[].class));
+    }
+
+    @Test
+    public void testLongArray() {
+        long[] longArr = {-128, 127};
+        assertEquals("[-128,127]", nullableJsonb.toJson(longArr));
+        assertArrayEquals(longArr, nullableJsonb.fromJson("[-128, 127]", long[].class));
+    }
+
+    @Test
+    public void testFloatArray() {
+        float[] floatArr = {-128, 127};
+        assertEquals("[-128.0,127.0]", nullableJsonb.toJson(floatArr));
+        assertArrayEquals(floatArr, nullableJsonb.fromJson("[-128.0, 127.0]", float[].class), 0f);
+    }
+
+    @Test
+    public void testDoubleArray() {
+        double[] doubleArr = {-128, 127};
+        assertEquals("[-128.0,127.0]", nullableJsonb.toJson(doubleArr));
+        assertArrayEquals(doubleArr, nullableJsonb.fromJson("[-128.0, 127.0]", double[].class), 0d);
+    }
+
+    public static class KeyValue {
+        public String field;
+
+        public KeyValue(String field) {
+            this.field = field;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/collections/CollectionsTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/collections/CollectionsTest.java
new file mode 100644
index 0000000..0e7fdb4
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/collections/CollectionsTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.collections;
+
+import static org.eclipse.yasson.Jsonbs.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.math.BigDecimal;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.generics.model.Circle;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Default mapping arrays/collections/enums tests.
+ *
+ * @author Dmitry Kornilov
+ */
+public class CollectionsTest {
+
+    @Test
+    public void testMarshallCollection() {
+        final Collection<Integer> collection = Arrays.asList(1, 2, 3);
+        assertEquals("[1,2,3]", nullableJsonb.toJson(collection));
+    }
+
+    @Test
+    public void testMarshallMap() {
+        Map<String, Integer> stringIntegerMap = new LinkedHashMap<>();
+        stringIntegerMap.put("1",1);
+        stringIntegerMap.put("2",2);
+        stringIntegerMap.put("3",3);
+
+        assertEquals("{\"1\":1,\"2\":2,\"3\":3}", nullableJsonb.toJson(stringIntegerMap));
+        assertEquals(stringIntegerMap, nullableJsonb.fromJson("{\"1\":1,\"2\":2,\"3\":3}", new LinkedHashMap<String, Integer>(){}.getClass().getGenericSuperclass()));
+        System.out.println();
+    }
+
+    @Test
+    public void testMarshallMapWithNulls() {
+        Map<String, String> mapWithNulls = new LinkedHashMap<>();
+        mapWithNulls.put("key1",null);
+        mapWithNulls.put("key2",null);
+        mapWithNulls.put("key3",null);
+
+        assertEquals("{\"key1\":null,\"key2\":null,\"key3\":null}", nullableJsonb.toJson(mapWithNulls));
+    }
+
+    @Test
+    public void testListOfNumbers() {
+        List<Number> numberList = new ArrayList<>();
+        numberList.add(1L);
+        numberList.add(2f);
+        numberList.add(10);
+
+        String result = nullableJsonb.toJson(numberList, new TestTypeToken<List<Number>>(){}.getType());
+    }
+
+    @Test
+    public void testListOfListsOfStrings() {
+        List<List<String>> listOfListsOfStrings = new ArrayList<>();
+        
+        for(int i = 0; i < 10; i++) {
+            List<String> stringList = new ArrayList<>();
+            stringList.add("first");
+            stringList.add("second");
+            stringList.add("third");
+            listOfListsOfStrings.add(stringList);
+        }
+        
+        final String expected = "[[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"],[\"first\",\"second\",\"third\"]]";
+        assertEquals(expected, nullableJsonb.toJson(listOfListsOfStrings));
+        assertEquals(listOfListsOfStrings, nullableJsonb.fromJson(expected, new ArrayList<List<String>>(){}.getClass().getGenericSuperclass()));
+    }
+
+    @Test
+    public void listOfMapsOfListsOfMaps() {
+        List<Map<String, List<Map<String, Integer>>>> listOfMapsOfListsOfMaps = new ArrayList<>();
+        
+        for(int i = 0; i < 3; i++) {
+            Map<String, List<Map<String, Integer>>> mapOfListsOfMap = new HashMap<>();
+            
+            for(int j = 0; j < 3; j++) {
+                List<Map<String, Integer>> listOfMaps = new ArrayList<>();
+                
+                for(int k = 0; k < 3; k++) {
+                    Map<String, Integer> stringIntegerMap = new HashMap<>();
+                    stringIntegerMap.put("first", 1);
+                    stringIntegerMap.put("second", 2);
+                    stringIntegerMap.put("third", 3);
+                    listOfMaps.add(stringIntegerMap);
+                }
+                mapOfListsOfMap.put(String.valueOf(j), listOfMaps);
+            }
+            listOfMapsOfListsOfMaps.add(mapOfListsOfMap);
+        }
+        
+        String expected = "[{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]},{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]},{\"0\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"1\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}],\"2\":[{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2},{\"third\":3,\"first\":1,\"second\":2}]}]";
+        assertEquals(expected, nullableJsonb.toJson(listOfMapsOfListsOfMaps));
+        ArrayList<Map<String, List<Map<String, Integer>>>> result = nullableJsonb.fromJson(expected, new TestTypeToken<ArrayList<Map<String, List<Map<String, Integer>>>>>(){}.getType());
+        assertEquals(listOfMapsOfListsOfMaps, result);
+    }
+
+    @Test
+    public void testAnyCollection() {
+        final Deque<String> deque = new ArrayDeque<>();
+        deque.add("dequeueFirst");
+        deque.add("dequeueSecond");
+        String expected = "[\"dequeueFirst\",\"dequeueSecond\"]";
+        assertEquals(expected, nullableJsonb.toJson(deque));
+        Deque<String> dequeueResult = nullableJsonb.fromJson(expected, new TestTypeToken<ArrayDeque<String>>(){}.getType());
+        assertEquals("dequeueFirst", dequeueResult.getFirst());
+        assertEquals("dequeueSecond", dequeueResult.getLast());
+
+        LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
+        linkedHashSet.add("setFirst");
+        linkedHashSet.add("setSecond");
+        expected = "[\"setFirst\",\"setSecond\"]";
+        assertEquals(expected, nullableJsonb.toJson(linkedHashSet));
+        LinkedHashSet<String> linkedHashSetResult = nullableJsonb.fromJson(expected, new TestTypeToken<LinkedHashSet<String>>(){}.getType());
+        Iterator<String> iterator = linkedHashSetResult.iterator();
+        assertEquals("setFirst", iterator.next());
+        assertEquals("setSecond", iterator.next());
+
+        LinkedList<String> linkedList = new LinkedList<>();
+        linkedList.add("listFirst");
+        linkedList.add("listSecond");
+        expected = "[\"listFirst\",\"listSecond\"]";
+        assertEquals(expected, nullableJsonb.toJson(linkedList));
+        LinkedList<String> linkedListResult = nullableJsonb.fromJson(expected, new TestTypeToken<LinkedList<String>>(){}.getType());
+        iterator = linkedListResult.iterator();
+        assertEquals("listFirst", iterator.next());
+        assertEquals("listSecond", iterator.next());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMarshallArray() {
+        //support of arrays of types that JSON Binding is able to serialize
+        //Byte[], Short[], Integer[] Long[], Float[], Double[], BigInteger[], BigDecimal[], Number[]
+        //Object[], JsonArray[], JsonObject[], JsonStructure[]
+        //String[], Character[]
+        //byte[], short[], int[], long[], float[], double[], char[], boolean[]
+        //java.net.URL[], java.net.URI[]
+        //Map[], Collection[], other collections ...
+        //enum, EnumSet, EnumMap
+        //support of multidimensional arrays
+
+        final Byte[] byteArray = {1, 2, 3};
+        assertEquals("[1,2,3]", nullableJsonb.toJson(byteArray));
+
+        final Integer[] integerArray = {1, 2, 3};
+        assertEquals("[1,2,3]", nullableJsonb.toJson(integerArray));
+
+        final String[] stringArray = {"first", "second", "third"};
+        assertEquals("[\"first\",\"second\",\"third\"]", nullableJsonb.toJson(stringArray));
+
+        Character[] charArr = {'a', 'b', 'c'};
+        assertEquals("[\"a\",\"b\",\"c\"]", nullableJsonb.toJson(charArr));
+
+        final byte[] bytePrimitivesArray = {1, 2, 3};
+        assertEquals("[1,2,3]", nullableJsonb.toJson(bytePrimitivesArray));
+
+        final int[] intArray = {1, 2, 3};
+        assertEquals("[1,2,3]", nullableJsonb.toJson(intArray));
+
+        final String[][] stringMultiArray = {{"first", "second"},{"third", "fourth"}};
+        assertEquals("[[\"first\",\"second\"],[\"third\",\"fourth\"]]", nullableJsonb.toJson(stringMultiArray));
+
+        final Map<String, Object>[][] mapMultiArray = new LinkedHashMap[2][2];
+        mapMultiArray[0][0] = new LinkedHashMap<>(1);
+        mapMultiArray[0][0].put("0", 0);
+        mapMultiArray[0][1] = new LinkedHashMap<>(1);
+        mapMultiArray[0][1].put("0", 1);
+        mapMultiArray[1][0] = new LinkedHashMap<>(1);
+        mapMultiArray[1][0].put("1", 0);
+        mapMultiArray[1][1] = new LinkedHashMap<>(1);
+        mapMultiArray[1][1].put("1", 1);
+
+        assertEquals("[[{\"0\":0},{\"0\":1}],[{\"1\":0},{\"1\":1}]]", nullableJsonb.toJson(mapMultiArray));
+    }
+
+    @Test
+    public void testMarshallEnumSet() {
+        final EnumSet<Language> languageEnumSet = EnumSet.of(Language.Czech, Language.Slovak);
+
+        final String result = nullableJsonb.toJson(languageEnumSet);
+        assertTrue("[\"Czech\",\"Slovak\"]".equals(result) || "[\"Slovak\",\"Czech\"]".equals(result));
+        assertEquals(languageEnumSet, nullableJsonb.fromJson(result, new TestTypeToken<EnumSet<Language>>() {}.getType()));
+    }
+
+    @Test
+    public void testMarshallEnumMap() {
+        final EnumMap<Language, String> languageEnumMap = new EnumMap<>(Language.class);
+        languageEnumMap.put(Language.Russian, "ru");
+        languageEnumMap.put(Language.English, "en");
+
+        final String result = nullableJsonb.toJson(languageEnumMap);
+        assertTrue("{\"Russian\":\"ru\",\"English\":\"en\"}".equals(result) ||
+                "{\"English\":\"en\",\"Russian\":\"ru\"}".equals(result));
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testRawCollection() {
+        List rawList = new ArrayList();
+        rawList.add("first");
+        Circle circle = new Circle();
+        circle.setRadius(2.0);
+        circle.setArea(1.0);
+        rawList.add(circle);
+
+        String expected = "[\"first\",{\"area\":1.0,\"radius\":2.0}]";
+        assertEquals(expected, nullableJsonb.toJson(rawList));
+        List result = nullableJsonb.fromJson(expected, List.class);
+        assertEquals("first", result.get(0));
+        assertEquals(new BigDecimal("2.0"), ((Map)result.get(1)).get("radius"));
+        assertEquals(new BigDecimal("1.0"), ((Map)result.get(1)).get("area"));
+    }
+
+    @Test
+    public void testNavigableMap() {
+        NavigableMap<String, String> map = new TreeMap<>();
+        map.put("first", "abc");
+        map.put("second", "def");
+        final String json = nullableJsonb.toJson(map);
+        assertEquals("{\"first\":\"abc\",\"second\":\"def\"}", json);
+
+        NavigableMap<String, String> result = nullableJsonb.fromJson(json, new TestTypeToken<NavigableMap<String, String>>() {}.getType());
+        assertEquals(TreeMap.class, result.getClass());
+        assertEquals("abc", result.get("first"));
+        assertEquals("def", result.get("second"));
+    }
+
+    @Test
+    public void testSortedMap() {
+        SortedMap<String, String> map = new TreeMap<>();
+        map.put("first", "abc");
+        map.put("second", "def");
+        final String json = nullableJsonb.toJson(map);
+        assertEquals("{\"first\":\"abc\",\"second\":\"def\"}", json);
+
+        SortedMap<String, String> result = nullableJsonb.fromJson(json, new TestTypeToken<SortedMap<String, String>>() {}.getType());
+        assertEquals(TreeMap.class, result.getClass());
+        assertEquals("abc", result.get("first"));
+        assertEquals("def", result.get("second"));
+    }
+    
+    public static class ConcurrentMapContainer {
+    	public ConcurrentMap<String, String> concurrentMap;
+    	public ConcurrentHashMap<String,String> concurrentHashMap;
+    	public ConcurrentNavigableMap<String, String> concurrentNavigableMap;
+    	public ConcurrentSkipListMap<String, String> concurrentSkipListMap;
+    }
+    
+    @Test
+    public void testConcurrentMaps() {
+    	// ConcurrentMap
+    	ConcurrentMapContainer c = new ConcurrentMapContainer();
+    	c.concurrentMap = new ConcurrentHashMap<String, String>();
+    	c.concurrentMap.put("foo", "fooVal");
+    	c.concurrentMap.put("bar", "barVal");
+    	String expectedJson = "{\"concurrentMap\":{\"bar\":\"barVal\",\"foo\":\"fooVal\"}}";
+    	assertEquals(expectedJson, defaultJsonb.toJson(c));
+    	assertEquals(c.concurrentMap, defaultJsonb.fromJson(expectedJson, ConcurrentMapContainer.class).concurrentMap);
+    	
+    	// ConcurrentHashMap
+    	c = new ConcurrentMapContainer();
+    	c.concurrentHashMap = new ConcurrentHashMap<String, String>();
+    	c.concurrentHashMap.put("foo", "fooVal2");
+    	c.concurrentHashMap.put("bar", "barVal2");
+    	expectedJson = "{\"concurrentHashMap\":{\"bar\":\"barVal2\",\"foo\":\"fooVal2\"}}";
+    	assertEquals(expectedJson, defaultJsonb.toJson(c));
+    	assertEquals(c.concurrentHashMap, defaultJsonb.fromJson(expectedJson, ConcurrentMapContainer.class).concurrentHashMap);
+    	
+    	// ConcurrentNavigableMap
+    	c = new ConcurrentMapContainer();
+    	c.concurrentNavigableMap = new ConcurrentSkipListMap<String, String>();
+    	c.concurrentNavigableMap.put("foo", "fooVal3");
+    	c.concurrentNavigableMap.put("bar", "barVal3");
+    	expectedJson = "{\"concurrentNavigableMap\":{\"bar\":\"barVal3\",\"foo\":\"fooVal3\"}}";
+    	assertEquals(expectedJson, defaultJsonb.toJson(c));
+    	assertEquals(c.concurrentNavigableMap, defaultJsonb.fromJson(expectedJson, ConcurrentMapContainer.class).concurrentNavigableMap);
+    	
+    	// ConcurrentSkipListMap
+    	c = new ConcurrentMapContainer();
+    	c.concurrentSkipListMap = new ConcurrentSkipListMap<String, String>();
+    	c.concurrentSkipListMap.put("foo", "fooVal4");
+    	c.concurrentSkipListMap.put("bar", "barVal4");
+    	expectedJson = "{\"concurrentSkipListMap\":{\"bar\":\"barVal4\",\"foo\":\"fooVal4\"}}";
+    	assertEquals(expectedJson, defaultJsonb.toJson(c));
+    	assertEquals(c.concurrentSkipListMap, defaultJsonb.fromJson(expectedJson, ConcurrentMapContainer.class).concurrentSkipListMap);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/collections/Language.java b/src/test/java/org/eclipse/yasson/defaultmapping/collections/Language.java
new file mode 100644
index 0000000..61ff949
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/collections/Language.java
@@ -0,0 +1,25 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.collections;
+
+/**
+ * Test enumeration.
+ *
+ * @author Dmitry Kornilov
+ */
+public enum Language {
+    English,
+    Russian,
+    Slovak,
+    Czech
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/collections/MapKeyTypesTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/collections/MapKeyTypesTest.java
new file mode 100644
index 0000000..9bdd7df
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/collections/MapKeyTypesTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.collections;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+import org.eclipse.yasson.Jsonbs;
+import org.eclipse.yasson.TestTypeToken;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Tests to verify proper map key serialization and deserialization.
+ */
+public class MapKeyTypesTest {
+
+    @Test
+    public void uuidMapKey() {
+        String expected = "{\"ccf3e2d3-3589-4c91-9ba9-1250ef515327\":{\"firstName\":\"FirstName1\",\"sureName\":\"SureName1\"},"
+                + "\"0bb54cee-d538-428b-9719-5cc38c988522\":{\"firstName\":\"FirstName2\",\"sureName\":\"SureName2\"}}";
+        Map<UUID, Person> map = new HashMap<>();
+        Person person = new Person();
+        person.firstName = "FirstName1";
+        person.sureName = "SureName1";
+        Person person2 = new Person();
+        person2.firstName = "FirstName2";
+        person2.sureName = "SureName2";
+        map.put(UUID.fromString("ccf3e2d3-3589-4c91-9ba9-1250ef515327"), person);
+        map.put(UUID.fromString("0bb54cee-d538-428b-9719-5cc38c988522"), person2);
+        assertEquals(expected, Jsonbs.defaultJsonb.toJson(map));
+        assertEquals(map, Jsonbs.defaultJsonb.fromJson(expected, new TestTypeToken<Map<UUID, Person>>() { }.getType()));
+    }
+
+    @Test
+    public void zonedDateTimeMapKey() {
+        ZonedDateTime zonedDateTime = ZonedDateTime.of(2020, 9, 14,
+                                                       9, 33, 12, 0,
+                                                       ZoneId.of("UTC"));
+        ZonedDateTime zonedDateTime2 = ZonedDateTime.of(2019, 8, 13,
+                                                        8, 32, 11, 1234,
+                                                        ZoneId.of("UTC"));
+        String expected = "{\"2020-09-14T09:33:12Z[UTC]\":{\"firstName\":\"FirstName1\","
+                + "\"sureName\":\"SureName1\"},"
+                + "\"2019-08-13T08:32:11.000001234Z[UTC]\":{\"firstName\":\"FirstName2\","
+                + "\"sureName\":\"SureName2\"}}";
+        Map<ZonedDateTime, Person> map = new HashMap<>();
+        Person person = new Person();
+        person.firstName = "FirstName1";
+        person.sureName = "SureName1";
+        Person person2 = new Person();
+        person2.firstName = "FirstName2";
+        person2.sureName = "SureName2";
+        map.put(zonedDateTime, person);
+        map.put(zonedDateTime2, person2);
+        assertEquals(expected, Jsonbs.defaultJsonb.toJson(map));
+        assertEquals(map, Jsonbs.defaultJsonb.fromJson(expected, new TestTypeToken<Map<ZonedDateTime, Person>>() { }.getType()));
+    }
+
+    public static final class Person {
+
+        public String firstName;
+        public String sureName;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            Person person = (Person) o;
+            return Objects.equals(firstName, person.firstName) &&
+                    Objects.equals(sureName, person.sureName);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(firstName, sureName);
+        }
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/DatesTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/DatesTest.java
new file mode 100644
index 0000000..287afca
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/DatesTest.java
@@ -0,0 +1,707 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Period;
+import java.time.YearMonth;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.YassonConfig;
+import org.eclipse.yasson.defaultmapping.dates.model.CalendarPojo;
+import org.eclipse.yasson.defaultmapping.dates.model.ClassLevelDateAnnotation;
+import org.eclipse.yasson.defaultmapping.dates.model.DatePojo;
+import org.eclipse.yasson.defaultmapping.dates.model.DateWithZonePojo;
+import org.eclipse.yasson.defaultmapping.dates.model.InstantPojo;
+import org.eclipse.yasson.defaultmapping.dates.model.LocalDatePojo;
+import org.eclipse.yasson.defaultmapping.dates.model.LocalDateTimePojo;
+import org.eclipse.yasson.defaultmapping.dates.model.LocalTimePojo;
+import org.eclipse.yasson.defaultmapping.dates.model.MonthDayPojo;
+import org.eclipse.yasson.defaultmapping.dates.model.OffsetDateTimePojo;
+import org.eclipse.yasson.defaultmapping.dates.model.OffsetTimePojo;
+import org.eclipse.yasson.defaultmapping.dates.model.YearMonthPojo;
+import org.eclipse.yasson.defaultmapping.dates.model.ZonedDateTimePojo;
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+import org.eclipse.yasson.internal.deserializer.types.SqlDateDeserializer;
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.bindingJsonb;
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * This class contains tests for marshalling/unmarshalling dates.
+ *
+ * @author Dmitry Kornilov
+ */
+public class DatesTest {
+    
+    private static LocalDate localDate = LocalDate.of(2018, 1, 31);
+
+    @SuppressWarnings("serial")
+	public static class LocalDateObj implements Serializable {
+        public LocalDate date = localDate;
+    }
+
+    @SuppressWarnings("serial")
+	public static class SqlDateObj implements Serializable {
+        public java.sql.Date sqlDate = java.sql.Date.valueOf("2018-01-31");
+        //no way for runtime to choose java.sql.Date deserializer here without a hint
+        @JsonbTypeDeserializer(SqlDateDeserializer.class)
+        public java.util.Date utilDate = java.sql.Date.valueOf("2018-01-31");
+
+    }
+    
+    public static class SqlDateFormatted {
+        @JsonbDateFormat(value = "yyyy-MM-dd")
+        public java.sql.Date sqlDate;
+    }
+
+    @Test
+    public void testSqlTimestamp() {
+        Timestamp expectedTimestamp = Timestamp.from(Instant.now());
+
+        String json = bindingJsonb.toJson(expectedTimestamp);
+
+        Timestamp timestamp = bindingJsonb.fromJson(json, Timestamp.class);
+        assertEquals(expectedTimestamp, timestamp);
+    }
+
+    @Test
+    public void testMarshallSqlDate() {
+        String jsonString = bindingJsonb.toJson(new SqlDateObj());
+        assertEquals("{\"sqlDate\":\"2018-01-31Z\",\"utilDate\":\"2018-01-31Z\"}", jsonString);
+    }
+
+    @Test
+    public void testUnmarshallSqlDate() {
+        SqlDateObj result = bindingJsonb.fromJson("{\"sqlDate\":\"2018-01-31Z\",\"utilDate\":\"2018-01-31Z\"}", SqlDateObj.class);
+        assertEquals("2018-01-31", result.sqlDate.toString());
+        assertEquals("2018-01-31", result.utilDate.toString());
+    }
+    
+    @Test
+    public void testSqlDateTimeZonesFormatted() {
+        testSqlDateWithTZFormatted(TimeZone.getTimeZone(ZoneId.of("Europe/Sofia")));
+        testSqlDateWithTZFormatted(TimeZone.getTimeZone(ZoneId.of("US/Hawaii")));
+        testSqlDateWithTZFormatted(TimeZone.getTimeZone(ZoneId.of("Australia/Sydney")));
+    }
+    
+    private void testSqlDateWithTZFormatted(TimeZone tz) {
+        final TimeZone originalTZ = TimeZone.getDefault();
+        TimeZone.setDefault(tz);
+        try {
+            Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDateFormat("yyyy-MM-dd", Locale.getDefault()));
+            java.sql.Date d = java.sql.Date.valueOf("1966-11-04");
+            String json = jsonb.toJson(d);
+            assertEquals("\"1966-11-04\"", json);
+            assertEquals("1966-11-04", jsonb.fromJson(json, java.sql.Date.class).toString());
+        } finally {
+            TimeZone.setDefault(originalTZ);
+        }
+    }
+    
+    @Test
+    public void testSqlDateTimeZonesMillis() {
+        testSqlDateTimeZonesMillis(TimeZone.getTimeZone(ZoneId.of("Europe/Sofia")), -99712800000L);
+        testSqlDateTimeZonesMillis(TimeZone.getTimeZone(ZoneId.of("US/Hawaii")), -99669600000L);
+        testSqlDateTimeZonesMillis(TimeZone.getTimeZone(ZoneId.of("Australia/Sydney")), -99741600000L);
+    }
+    
+    private void testSqlDateTimeZonesMillis(TimeZone tz, long expectedMs) {
+        final TimeZone originalTZ = TimeZone.getDefault();
+        TimeZone.setDefault(tz);
+        JsonbConfig jsonbConfig = new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.getDefault());
+        try (Jsonb jsonb = JsonbBuilder.create(jsonbConfig)) {
+            java.sql.Date d = java.sql.Date.valueOf("1966-11-04");
+            String json = jsonb.toJson(d);
+            assertEquals("" + expectedMs, json);
+            assertEquals(d, jsonb.fromJson(json, java.sql.Date.class));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            TimeZone.setDefault(originalTZ);
+        }
+    }
+    
+    @Test
+    public void testSqlDateTimeZones() {
+        testSqlDateWithTZ(TimeZone.getTimeZone(ZoneId.of("Europe/Sofia")));
+        testSqlDateWithTZ(TimeZone.getTimeZone(ZoneId.of("US/Hawaii")));
+        testSqlDateWithTZ(TimeZone.getTimeZone(ZoneId.of("Australia/Sydney")));
+    }
+    
+    private void testSqlDateWithTZ(TimeZone tz) {
+        final TimeZone originalTZ = TimeZone.getDefault();
+        TimeZone.setDefault(tz);
+        try {
+            java.sql.Date d = java.sql.Date.valueOf("1966-11-04");
+            String json = defaultJsonb.toJson(d);
+            assertEquals("\"1966-11-04Z\"", json);
+            assertEquals("1966-11-04", defaultJsonb.fromJson(json, java.sql.Date.class).toString());
+        } finally {
+            TimeZone.setDefault(originalTZ);
+        }
+    }
+
+    @Test
+    public void testMarshallLocalDate() {
+        String jsonString = bindingJsonb.toJson(new LocalDateObj());
+        if (jsonString.contains("T")) {
+            fail("JSON contains time for a non Date that doesn't include time");
+        }
+    }
+
+    @Test
+    public void testUnmarshallLocalDate() {
+        LocalDateObj localDateObj = bindingJsonb.fromJson("{\"date\":\"2018-01-31\"}", LocalDateObj.class);
+        assertEquals(localDate, localDateObj.date);
+    }
+
+    @Test
+    public void testDate() throws ParseException {
+        final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
+        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+        final Date parsedDate = sdf.parse("04.03.2015");
+
+        final DatePojo pojo = new DatePojo();
+        pojo.customDate = parsedDate;
+        pojo.defaultFormatted = parsedDate;
+        pojo.millisFormatted = parsedDate;
+
+        // marshal to ISO format
+        final String expected = "{\"defaultFormatted\":\"2015-03-04T00:00:00Z[UTC]\"," +
+                "\"millisFormatted\":" + parsedDate.getTime()+ "," +
+                "\"customDate\":\"00:00:00 | 04-03-2015\"}";
+        assertEquals(expected, bindingJsonb.toJson(pojo));
+
+        final DatePojo result = bindingJsonb.fromJson(expected, DatePojo.class);
+        assertEquals(parsedDate, result.customDate);
+        assertEquals(parsedDate, result.defaultFormatted);
+        assertEquals(parsedDate, result.millisFormatted);
+    }
+
+    @Test
+    public void testDateWithZoneOffset() throws ParseException {
+        // Test for Yasson-172
+        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
+        final Date parsedDate = sdf.parse("2018-11-02T00:00:00+01:00");
+    	
+    	String jsonDateWithZone = "{\"dateWithZone\":\"2018-11-02T00:00:00+01:00\"}";
+    	final DateWithZonePojo result = bindingJsonb.fromJson(jsonDateWithZone, DateWithZonePojo.class);
+    	
+    	assertEquals(parsedDate, result.dateWithZone);
+    }
+    
+    @Test
+    public void testDateWithZoneId() throws ParseException {
+        // Test for Yasson-172
+        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
+        final Date parsedDate = sdf.parse("2018-11-02T00:00:00+01:00");
+    	
+    	String jsonDateWithZone = "{\"dateWithZone\":\"2018-11-02T00:00:00+01:00[Europe/Berlin]\"}";
+    	final DateWithZonePojo result = bindingJsonb.fromJson(jsonDateWithZone, DateWithZonePojo.class);
+    	
+    	assertEquals(parsedDate, result.dateWithZone);
+    }
+    
+    @Test
+    public void testCalendar() {
+        final Calendar timeCalendar = new Calendar.Builder()
+                .setDate(2015, Calendar.APRIL, 3)
+                .setTimeOfDay(11, 11, 10)
+                .setTimeZone(TimeZone.getTimeZone("Europe/Prague"))
+                .build();
+
+        final CalendarPojo calendarPojo = new CalendarPojo();
+        calendarPojo.customCalendar = timeCalendar;
+        calendarPojo.defaultFormatted = timeCalendar;
+        calendarPojo.millisFormatted = timeCalendar;
+
+        // marshal to ISO_DATE
+        final String expected = "{\"defaultFormatted\":\"2015-04-03T11:11:10+02:00[Europe/Prague]\"," +
+                "\"millisFormatted\":1428052270000," +
+                "\"customCalendar\":\"11:11:10 | 03-04-2015, +0200\"}";
+        assertEquals(expected, bindingJsonb.toJson(calendarPojo));
+
+        // marshal to ISO_DATE_TIME
+        final CalendarPojo result = bindingJsonb.fromJson(expected, CalendarPojo.class);
+        assertEquals(timeCalendar.getTime(), result.customCalendar.getTime());
+        assertEquals(timeCalendar.getTime(), result.millisFormatted.getTime());
+        assertEquals(timeCalendar.getTime(), result.defaultFormatted.getTime());
+    }
+
+    @Test
+    public void testCalendarWithoutTime() {
+        ScalarValueWrapper<Calendar> result = bindingJsonb.fromJson("{\"value\":\"2015-04-03+01:00\"}", new TestTypeToken<ScalarValueWrapper<Calendar>>(){}.getType());
+        assertEquals(2015, result.getValue().get(Calendar.YEAR));
+        assertEquals(3, result.getValue().get(Calendar.MONTH));
+        assertEquals(3, result.getValue().get(Calendar.DAY_OF_MONTH));
+        assertEquals(0, result.getValue().get(Calendar.HOUR_OF_DAY));
+        assertEquals(0, result.getValue().get(Calendar.MINUTE));
+        assertEquals(0, result.getValue().get(Calendar.SECOND));
+        assertEquals("GMT+01:00", result.getValue().getTimeZone().toZoneId().toString());
+    }
+
+    @Test
+    public void testCalendarWithNonDefaultTimeZone() {
+        final ZoneId zoneId = ZoneId.of("Europe/Prague");
+        final Calendar cal = new Calendar.Builder()
+                .setDate(2015, Calendar.APRIL, 3)
+                .setTimeOfDay(10, 10, 10)
+                .setTimeZone(TimeZone.getTimeZone(zoneId))
+                .build();
+
+        final CalendarPojo calendarPojo = new CalendarPojo();
+        calendarPojo.customCalendar = cal;
+        calendarPojo.defaultFormatted = cal;
+        calendarPojo.millisFormatted = cal;
+
+        // marshal to ISO_DATE
+        final String expected = "{\"defaultFormatted\":\"2015-04-03T10:10:10+02:00[" + zoneId + "]\"," +
+                "\"millisFormatted\":" + cal.getTimeInMillis() + "," +
+                "\"customCalendar\":\"10:10:10 | 03-04-2015, +0200\"}";
+        assertEquals(expected, bindingJsonb.toJson(calendarPojo));
+
+        // marshal to ISO_DATE_TIME
+        final CalendarPojo result = bindingJsonb.fromJson(expected, CalendarPojo.class);
+        assertEquals(cal.getTime(), result.customCalendar.getTime());
+        assertEquals(cal.getTime(), result.millisFormatted.getTime());
+        assertEquals(cal.getTime(), result.defaultFormatted.getTime());
+    }
+
+    @Test
+    public void testMarshalGregorianCalendar() {
+        final Calendar cal = GregorianCalendar.getInstance();
+        cal.clear();
+        cal.set(2015, Calendar.APRIL, 3);
+        cal.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+        // marshal to ISO_DATE
+        assertEquals("{\"value\":\"2015-04-03Z\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(cal)));
+
+        // marshal to ISO_DATE_TIME
+        final Calendar dateTimeGregorianCalendar = new Calendar.Builder().setDate(2015, 3, 3)
+                .setTimeZone(TimeZone.getTimeZone("Europe/Prague"))
+                .build();
+        assertEquals("{\"value\":\"2015-04-03T00:00:00+02:00[Europe/Prague]\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(dateTimeGregorianCalendar)));
+    }
+
+    @Test
+    public void testMarshalTimeZone() {
+        assertEquals("{\"value\":\"Europe/Prague\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(TimeZone.getTimeZone("Europe/Prague"))));
+        assertEquals("{\"value\":\"Europe/Prague\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(SimpleTimeZone.getTimeZone("Europe/Prague"))));
+    }
+
+    @Test
+    public void testMarshalInstant() {
+        final Instant instant = Instant.parse("2015-03-03T23:00:00Z");
+        InstantPojo instantPojo = new InstantPojo(instant);
+
+        final String expected = "{\"defaultFormatted\":\"2015-03-03T23:00:00Z\"," +
+                "\"millisFormatted\":1425423600000," +
+                "\"instant\":\"23:00:00 | 03-03-2015\"}";
+        assertEquals(expected, bindingJsonb.toJson(instantPojo));
+
+        InstantPojo result = bindingJsonb.fromJson(expected, InstantPojo.class);
+        assertEquals(instant, result.defaultFormatted);
+        assertEquals(instant, result.millisFormatted);
+        assertEquals(instant, result.instant);
+    }
+
+    @Test
+    public void testDateFormattedAsMillisInString() {
+        Jsonb jsonb = JsonbBuilder.create(new YassonConfig().withTimeInMillisAsAString(true));
+        final Instant instant = Instant.parse("2015-03-03T23:00:00Z");
+        InstantPojo instantPojo = new InstantPojo(instant);
+
+        final String expected = "{\"defaultFormatted\":\"2015-03-03T23:00:00Z\"," +
+                "\"millisFormatted\":\"1425423600000\"," +
+                "\"instant\":\"23:00:00 | 03-03-2015\"}";
+        assertEquals(expected, jsonb.toJson(instantPojo));
+
+        InstantPojo result = jsonb.fromJson(expected, InstantPojo.class);
+        assertEquals(instant, result.defaultFormatted);
+        assertEquals(instant, result.millisFormatted);
+        assertEquals(instant, result.instant);
+    }
+
+    @Test
+    public void testMarshalDuration() {
+        assertEquals("{\"value\":\"PT5H4M\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(Duration.ofHours(5).plusMinutes(4))));
+    }
+
+    @Test
+    public void testMarshalPeriod() {
+        final Period period = Period.between(LocalDate.of(1960, Month.JANUARY, 1), LocalDate.of(1970, Month.JANUARY, 1));
+        final ScalarValueWrapper<Period> value = new ScalarValueWrapper<>(period);
+        assertEquals("{\"value\":\"P10Y\"}", bindingJsonb.toJson(value));
+    }
+
+    @Test
+    public void testLocalDate() {
+        final LocalDate localDate = LocalDate.of(2015, Month.APRIL, 10);
+        final LocalDatePojo pojo = new LocalDatePojo(localDate);
+
+        // Get proper milliseconds
+        final long millis = localDate.atStartOfDay(ZoneId.of("Z")).toInstant().toEpochMilli();
+
+        final String expected = "{\"defaultFormatted\":\"2015-04-10\"," +
+                "\"millisFormatted\":" + millis + "," +
+                "\"customLocalDate\":\"10-04-2015\"}";
+        assertEquals(expected, bindingJsonb.toJson(pojo));
+
+        final LocalDatePojo result = bindingJsonb.fromJson(expected, LocalDatePojo.class);
+        assertEquals(localDate, result.customLocalDate);
+        assertEquals(localDate, result.millisFormatted);
+        assertEquals(localDate, result.defaultFormatted);
+    }
+
+    @Test
+    public void testlLocalTime() {
+        final Jsonb jsonb = getJsonbWithMillisIgnored();
+        final LocalTime localTime = LocalTime.of(22, 33);
+        final LocalTimePojo localTimePojo = new LocalTimePojo(localTime);
+        final String expected = "{\"defaultFormatted\":\"22:33:00\",\"localTime\":\"22:33:00\"}";
+        assertEquals(expected, jsonb.toJson(localTimePojo));
+
+        final LocalTimePojo result = jsonb.fromJson(expected, LocalTimePojo.class);
+        assertEquals(localTime, result.defaultFormatted);
+        assertEquals(localTime, result.localTime);
+    }
+
+    private Jsonb getJsonbWithMillisIgnored() {
+        final JsonbConfig config = new JsonbConfig();
+        config.withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() {
+            @Override
+            public boolean isVisible(Field field) {
+                return !field.getName().startsWith("millis");
+            }
+
+            @Override
+            public boolean isVisible(Method method) {
+                return false;
+            }
+        });
+        return JsonbBuilder.create(config);
+    }
+
+    @Test
+    public void testLocalDateTime() {
+        final LocalDateTime dateTime = LocalDateTime.of(2015, 2, 16, 13, 21);
+        final LocalDateTimePojo pojo = new LocalDateTimePojo(dateTime);
+
+        // Get proper milliseconds
+        final long millis = dateTime.atZone(ZoneId.of("Z")).toInstant().toEpochMilli();
+
+        final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00\"," +
+                "\"millisFormatted\":" + millis + "," +
+                "\"customLocalDate\":\"16-02-2015--00:21:13\"}";
+        assertEquals(expected, bindingJsonb.toJson(pojo));
+
+        final LocalDateTimePojo result = bindingJsonb.fromJson(expected, LocalDateTimePojo.class);
+        assertEquals(dateTime, result.defaultFormatted);
+        assertEquals(dateTime, result.millisFormatted);
+        assertEquals(dateTime, result.customLocalDate);
+    }
+
+    @Test
+    public void testLocalDateTimeWithoutConfig() {
+        final LocalDateTime dateTime = LocalDateTime.of(2015, 2, 16, 13, 21);
+        final ScalarValueWrapper<LocalDateTime> pojo = new ScalarValueWrapper<>();
+        pojo.setValue(dateTime);
+
+        final String expected = "{\"value\":\"2015-02-16T13:21:00\"}";
+        assertEquals(expected, bindingJsonb.toJson(pojo));
+
+        final ScalarValueWrapper<LocalDateTime> result = bindingJsonb.fromJson(expected, new TestTypeToken<ScalarValueWrapper<LocalDateTime>>(){}.getType());
+        assertEquals(dateTime, result.getValue());
+    }
+
+    @Test
+    public void testDifferentConfigsLocalDateTime() {
+        final LocalDateTime dateTime = LocalDateTime.of(2015, 2, 16, 13, 21);
+        final long millis = dateTime.atZone(ZoneId.of("Z")).toInstant().toEpochMilli();
+        final ScalarValueWrapper<LocalDateTime> pojo = new ScalarValueWrapper<>();
+        pojo.setValue(dateTime);
+
+        final String expected = "{\"value\":\"2015-02-16T13:21:00\"}";
+        assertEquals(expected, bindingJsonb.toJson(pojo));
+
+        final Jsonb jsonbCustom = JsonbBuilder.create(new JsonbConfig().withDateFormat(JsonbDateFormat.TIME_IN_MILLIS, Locale.FRENCH));
+        assertEquals("{\"value\":" + millis + "}", jsonbCustom.toJson(pojo));
+
+        ScalarValueWrapper<LocalDateTime> result = bindingJsonb.fromJson(expected, new TestTypeToken<ScalarValueWrapper<LocalDateTime>>(){}.getType());
+        assertEquals(dateTime, result.getValue());
+
+        result = jsonbCustom.fromJson("{\"value\":\"" + millis + "\"}", new TestTypeToken<ScalarValueWrapper<LocalDateTime>>(){}.getType());
+        assertEquals(dateTime, result.getValue());
+    }
+
+    @Test
+    public void testZonedDateTime() {
+        final ZoneId zone = ZoneId.of("Asia/Almaty");
+        final ZonedDateTime dateTime = ZonedDateTime.of(2015, 2, 16, 13, 21, 0, 0, zone);
+        final ZonedDateTimePojo pojo = new ZonedDateTimePojo(dateTime);
+
+        final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00+06:00[" + zone + "]\"," +
+                "\"millisFormatted\":" + dateTime.toInstant().toEpochMilli() + "," +
+                "\"customZonedDate\":\"+06" + zone + " | 16-02-2015--00:21:13\"}";
+        assertEquals(expected, bindingJsonb.toJson(pojo));
+
+        final ZonedDateTimePojo result = bindingJsonb.fromJson(expected, ZonedDateTimePojo.class);
+        assertEquals(dateTime, result.defaultFormatted);
+        assertEquals(dateTime, result.customZonedDate);
+
+        // time zone and seconds omitted
+        final ZonedDateTimePojo result1 = bindingJsonb.fromJson("{\"defaultFormatted\":\"2015-02-16T13:21+06:00\"}", ZonedDateTimePojo.class);
+        assertEquals(dateTime.getHour(), result1.defaultFormatted.getHour());
+        assertEquals(dateTime.getOffset(), result1.defaultFormatted.getOffset());
+    }
+
+    @Test
+    public void testMarshalZoneId() {
+        assertEquals("{\"value\":\"Europe/Prague\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(ZoneId.of("Europe/Prague"))));
+    }
+
+    @Test
+    public void testMarshalZoneOffset() {
+        assertEquals("{\"value\":\"+02:00\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(ZoneOffset.of("+02:00"))));
+    }
+
+    @Test
+    public void testMarshalOffsetDateTime() {
+        final OffsetDateTime dateTime = OffsetDateTime.of(2015, 2, 16, 13, 21, 0, 0, ZoneOffset.of("+05:00"));
+        final OffsetDateTimePojo pojo = new OffsetDateTimePojo(dateTime);
+
+        final String expected = "{\"defaultFormatted\":\"2015-02-16T13:21:00+05:00\"," +
+                "\"millisFormatted\":1424074860000," +
+                "\"offsetDateTime\":\"+0500 16-02-2015--00:21:13\"}";
+        assertEquals(expected, bindingJsonb.toJson(pojo));
+
+        final OffsetDateTimePojo result = bindingJsonb.fromJson(expected, OffsetDateTimePojo.class);
+        assertEquals(dateTime, result.defaultFormatted);
+        assertEquals(dateTime, result.offsetDateTime);
+    }
+
+    @Test
+    public void testMarshalOffsetTime() {
+        final Jsonb jsonb = getJsonbWithMillisIgnored();
+        final OffsetTime dateTime = OffsetTime.of(13, 21, 15, 0, ZoneOffset.of("+05:00"));
+        final OffsetTimePojo pojo = new OffsetTimePojo(dateTime);
+
+        final String expected = "{\"defaultFormatted\":\"13:21:15+05:00\",\"offsetTime\":\"13:21:15+0500\"}";
+        assertEquals(expected, jsonb.toJson(pojo));
+
+        final OffsetTimePojo result = jsonb.fromJson(expected, OffsetTimePojo.class);
+        assertEquals(dateTime, result.defaultFormatted);
+        assertEquals(dateTime, result.offsetTime);
+    }
+
+    @Test
+    public void testClassLevel() throws ParseException {
+        final ClassLevelDateAnnotation pojo = new ClassLevelDateAnnotation();
+        final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
+        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+        pojo.date = sdf.parse("04.03.2015");
+
+        final ZoneId zone = ZoneId.of("Asia/Almaty");
+        pojo.calendar = new Calendar.Builder().setDate(2015, Calendar.APRIL, 3).setTimeOfDay(11, 11, 10).setTimeZone(TimeZone.getTimeZone(zone)).build();
+        pojo.zonedDateTime = ZonedDateTime.of(2015, 4, 3, 13, 21, 0, 0, zone);
+        pojo.defaultZoned = pojo.zonedDateTime;
+        pojo.localDateTime  = LocalDateTime.of(2015, 4, 3, 13, 21, 0, 0);
+
+        final String expected = "{\"date\":\"04-03-2015 00:00:00\"," +
+                "\"localDateTime\":\"03-04-2015 13:21:00\"," +
+                "\"calendar\":\"+06 ALMT ven. avril 03-04-2015 11:11:10\"," +
+                "\"defaultZoned\":\"2015-04-03T13:21:00+06:00[Asia/Almaty]\"," +
+                "\"zonedDateTime\":\"+06 ALMT ven. avril 03-04-2015 13:21:00\"}";
+        assertEquals(expected, bindingJsonb.toJson(pojo));
+
+        final ClassLevelDateAnnotation result = bindingJsonb.fromJson(expected, ClassLevelDateAnnotation.class);
+        assertEquals(pojo.date, result.date);
+        assertEquals(pojo.localDateTime, result.localDateTime);
+        assertEquals(pojo.calendar.getTime(), result.calendar.getTime());
+        assertEquals(pojo.zonedDateTime, result.zonedDateTime);
+    }
+
+    @Test
+    public void testGlobalConfigDateFormat() {
+        final JsonbConfig config = new JsonbConfig();
+        config.withDateFormat("X z E MMMM dd-MM-yyyy HH:mm:ss", Locale.FRENCH);
+
+        final Jsonb jsonb = JsonbBuilder.create(config);
+
+        final ZonedDateTime dateTime = ZonedDateTime.of(2015, 4, 3, 13, 21, 0, 0, ZoneId.of("Asia/Almaty"));
+        final String expected = "{\"value\":\"+06 ALMT ven. avril 03-04-2015 13:21:00\"}";
+        assertEquals(expected, jsonb.toJson(new ScalarValueWrapper<>(dateTime)));
+
+        final ScalarValueWrapper<ZonedDateTime> result = jsonb.fromJson(expected, new TestTypeToken<ScalarValueWrapper<ZonedDateTime>>(){}.getType());
+        assertEquals(dateTime, result.getValue());
+    }
+
+    @Test
+    public void testDateFrenchLocale() {
+        String format = "E DD MMM yyyy HH:mm:ss z";
+        Locale locale = Locale.forLanguageTag("fr-FR");
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDateFormat(format, locale));
+
+        final ScalarValueWrapper<Date> result = jsonb.fromJson("{ \"value\" : \"lun. 93 avr. 2017 16:51:12 CEST\" }", new TestTypeToken<ScalarValueWrapper<Date>>(){}.getType());
+
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
+        final Instant instant = Instant.from(formatter.withLocale(locale).parse("lun. 93 avr. 2017 16:51:12 CEST"));
+        assertEquals(instant, result.getValue().toInstant());
+    }
+
+    @Test
+    public void testSimpleTimeZone() {
+        String json = bindingJsonb.toJson(new ScalarValueWrapper<>(new SimpleTimeZone(0, "America/Los_Angeles")));
+        assertEquals("{\"value\":\"America/Los_Angeles\"}", json);
+
+        ScalarValueWrapper<TimeZone> result = bindingJsonb.fromJson(json, new TestTypeToken<ScalarValueWrapper<TimeZone>>() {
+        }.getType());
+        assertEquals("America/Los_Angeles", result.getValue().getID());
+        assertEquals(LocalDateTime.now().atZone(ZoneId.of("America/Los_Angeles")).getOffset().getTotalSeconds() * 1000,
+                result.getValue().getOffset(System.currentTimeMillis()));
+    }
+
+    @Test
+    public void testDateInMap() {
+
+        JsonbConfig config = new JsonbConfig()
+                .withDateFormat("yyyy", Locale.ENGLISH);
+
+        Jsonb jsonb = JsonbBuilder.create(config);
+        LocalDate localDate = LocalDate.of(2017, 9, 14);
+
+        DateInMapPojo pojo = new DateInMapPojo();
+        pojo.setLocalDate(localDate);
+        pojo.setDateMap(new HashMap<>());
+        pojo.getDateMap().put("first", localDate);
+
+        String json = jsonb.toJson(pojo);
+
+        assertEquals("{\"dateMap\":{\"first\":\"2017\"},\"localDate\":\"2017\"}", json);
+
+        config = new JsonbConfig()
+                .withDateFormat("dd.MM.yyyy", Locale.ENGLISH);
+        jsonb = JsonbBuilder.create(config);
+        DateInMapPojo result = jsonb.fromJson("{\"dateMap\":{\"first\":\"01.01.2017\"},\"localDate\":\"01.01.2017\"}", DateInMapPojo.class);
+        assertEquals(LocalDate.of(2017,1,1), result.localDate);
+        assertEquals(LocalDate.of(2017,1,1), result.dateMap.get("first"));
+    }
+
+    @Test
+    public void testYearMonth() {
+        YearMonthPojo yearMonthPojo = new YearMonthPojo();
+        yearMonthPojo.yearMonth = YearMonth.of(2019, Month.MAY);
+        yearMonthPojo.yearMonthWithFormatter = YearMonth.of(2019, Month.MAY);
+
+        String expected = "{\"yearMonth\":\"2019-05\",\"yearMonthWithFormatter\":\"05-2019\"}";
+        String serialized = bindingJsonb.toJson(yearMonthPojo);
+        assertEquals(expected, serialized);
+
+        YearMonthPojo deserialized = bindingJsonb.fromJson(expected, YearMonthPojo.class);
+        assertEquals(yearMonthPojo, deserialized);
+    }
+
+    @Test
+    public void testMonthDay() {
+        MonthDayPojo monthDay = new MonthDayPojo();
+        monthDay.monthDay = MonthDay.of(Month.MAY, 2);
+        monthDay.monthDayWithFormatter = MonthDay.of(Month.MAY, 2);
+
+        String expected = "{\"monthDay\":\"--05-02\",\"monthDayWithFormatter\":\"02-05\"}";
+        String serialized = bindingJsonb.toJson(monthDay);
+        assertEquals(expected, serialized);
+
+        MonthDayPojo deserialized = bindingJsonb.fromJson(expected, MonthDayPojo.class);
+        assertEquals(monthDay, deserialized);
+    }
+
+    @Test
+    public void testXMLGregorianCalendar() throws DatatypeConfigurationException {
+        final Calendar calendar = new Calendar.Builder()
+                .setDate(2015, Calendar.APRIL, 3)
+                .setTimeOfDay(11, 11, 10)
+                .setTimeZone(TimeZone.getTimeZone("Europe/Prague"))
+                .build();
+
+        final XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
+                .newXMLGregorianCalendar((GregorianCalendar) calendar);
+
+        String serialized = bindingJsonb.toJson(xmlGregorianCalendar);
+
+        assertEquals("\"2015-04-03T11:11:10+02:00[GMT+02:00]\"", serialized);
+        assertEquals(xmlGregorianCalendar, bindingJsonb.fromJson(serialized, XMLGregorianCalendar.class));
+    }
+
+
+    public static class DateInMapPojo {
+        private LocalDate localDate;
+        private Map<String, LocalDate> dateMap;
+
+        public LocalDate getLocalDate() {
+            return localDate;
+        }
+
+        public void setLocalDate(LocalDate localDate) {
+            this.localDate = localDate;
+        }
+
+        public Map<String, LocalDate> getDateMap() {
+            return dateMap;
+        }
+
+        public void setDateMap(Map<String, LocalDate> dateMap) {
+            this.dateMap = dateMap;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/AbstractDateTimePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/AbstractDateTimePojo.java
new file mode 100644
index 0000000..ee62744
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/AbstractDateTimePojo.java
@@ -0,0 +1,39 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+
+/**
+ * Common parent class for testing date objects.
+ * Contains {@link JsonbDateFormat} DEFAULT_FORMAT and TIME_IN_MILLIS annotated date objects.
+ *
+ * @author Roman Grigoriadi
+ */
+public class AbstractDateTimePojo<T> {
+
+    public AbstractDateTimePojo() {
+    }
+
+    public AbstractDateTimePojo(T dateObj) {
+        this.defaultFormatted = dateObj;
+        this.millisFormatted = dateObj;
+    }
+
+    @JsonbDateFormat(JsonbDateFormat.DEFAULT_FORMAT)
+    public T defaultFormatted;
+
+    @JsonbDateFormat(JsonbDateFormat.TIME_IN_MILLIS)
+    public T millisFormatted;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/CalendarPojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/CalendarPojo.java
new file mode 100644
index 0000000..93241d7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/CalendarPojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.util.Calendar;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CalendarPojo extends AbstractDateTimePojo<Calendar> {
+
+    public CalendarPojo() {
+    }
+
+    public CalendarPojo(Calendar dateObj) {
+        super(dateObj);
+        this.customCalendar = dateObj;
+    }
+
+    @JsonbDateFormat("HH:mm:ss | dd-MM-yyyy, Z")
+    public Calendar customCalendar;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ClassLevelDateAnnotation.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ClassLevelDateAnnotation.java
new file mode 100644
index 0000000..7f3cae9
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ClassLevelDateAnnotation.java
@@ -0,0 +1,32 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbDateFormat(value = "X z E MMMM dd-MM-yyyy HH:mm:ss", locale = "fr")
+public class ClassLevelDateAnnotation extends ClassLevelDateAnnotationParent {
+
+    public ZonedDateTime zonedDateTime;
+
+    public Calendar calendar;
+
+    @JsonbDateFormat(value = JsonbDateFormat.DEFAULT_FORMAT)
+    public ZonedDateTime defaultZoned;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ClassLevelDateAnnotationParent.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ClassLevelDateAnnotationParent.java
new file mode 100644
index 0000000..9a2b512
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ClassLevelDateAnnotationParent.java
@@ -0,0 +1,29 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbDateFormat("dd-MM-yyyy HH:mm:ss")
+public class ClassLevelDateAnnotationParent {
+
+    public Date date;
+
+    public LocalDateTime localDateTime;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/CollectionDatePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/CollectionDatePojo.java
new file mode 100644
index 0000000..82fa113
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/CollectionDatePojo.java
@@ -0,0 +1,26 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CollectionDatePojo {
+
+    @JsonbDateFormat("HH:mm:ss | dd-MM-yyyy")
+    public List<Date> dates;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/DatePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/DatePojo.java
new file mode 100644
index 0000000..7b1c551
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/DatePojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.util.Date;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class DatePojo extends AbstractDateTimePojo<Date> {
+
+    public DatePojo() {
+    }
+
+    public DatePojo(Date dateObj) {
+        super(dateObj);
+        this.customDate = dateObj;
+    }
+
+    @JsonbDateFormat("HH:mm:ss | dd-MM-yyyy")
+    public Date customDate;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/DateWithZonePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/DateWithZonePojo.java
new file mode 100644
index 0000000..a7ff525
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/DateWithZonePojo.java
@@ -0,0 +1,31 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import java.util.Date;
+
+/**
+ * @author Dennis Kriechel
+ */
+public class DateWithZonePojo extends AbstractDateTimePojo<Date> {
+
+    public DateWithZonePojo() {
+    }
+
+    public DateWithZonePojo(Date dateObj) {
+        super(dateObj);
+        this.dateWithZone = dateObj;
+    }
+
+    public Date dateWithZone;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/InstantPojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/InstantPojo.java
new file mode 100644
index 0000000..9acdf9b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/InstantPojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.Instant;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class InstantPojo extends AbstractDateTimePojo<Instant> {
+
+    public InstantPojo() {
+    }
+
+    public InstantPojo(Instant dateObj) {
+        super(dateObj);
+        this.instant = dateObj;
+    }
+
+    @JsonbDateFormat("HH:mm:ss | dd-MM-yyyy")
+    public Instant instant;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalDatePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalDatePojo.java
new file mode 100644
index 0000000..e736768
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalDatePojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.LocalDate;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class LocalDatePojo extends AbstractDateTimePojo<LocalDate> {
+
+    public LocalDatePojo() {
+    }
+
+    public LocalDatePojo(LocalDate date) {
+        super(date);
+        this.customLocalDate = date;
+    }
+
+    @JsonbDateFormat("dd-MM-yyyy")
+    public LocalDate customLocalDate;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalDateTimePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalDateTimePojo.java
new file mode 100644
index 0000000..9da6679
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalDateTimePojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.LocalDateTime;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class LocalDateTimePojo extends AbstractDateTimePojo<LocalDateTime> {
+
+    public LocalDateTimePojo() {
+    }
+
+    public LocalDateTimePojo(LocalDateTime date) {
+        super(date);
+        this.customLocalDate = date;
+    }
+
+    @JsonbDateFormat("dd-MM-yyyy--ss:mm:HH")
+    public LocalDateTime customLocalDate;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalTimePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalTimePojo.java
new file mode 100644
index 0000000..b7899df
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/LocalTimePojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.LocalTime;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class LocalTimePojo extends AbstractDateTimePojo<LocalTime> {
+
+    public LocalTimePojo() {
+    }
+
+    public LocalTimePojo(LocalTime date) {
+        super(date);
+        this.localTime = date;
+    }
+
+    @JsonbDateFormat("HH:mm:ss")
+    public LocalTime localTime;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/MonthDayPojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/MonthDayPojo.java
new file mode 100644
index 0000000..5fcabd3
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/MonthDayPojo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import java.time.MonthDay;
+import java.util.Objects;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+
+/**
+ * Pojo object of the {@link MonthDay}.
+ */
+public class MonthDayPojo {
+
+    public MonthDay monthDay;
+
+    @JsonbDateFormat("dd-MM")
+    public MonthDay monthDayWithFormatter;
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        MonthDayPojo that = (MonthDayPojo) o;
+        return Objects.equals(monthDay, that.monthDay) && Objects
+                .equals(monthDayWithFormatter, that.monthDayWithFormatter);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(monthDay, monthDayWithFormatter);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/OffsetDateTimePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/OffsetDateTimePojo.java
new file mode 100644
index 0000000..2c2fa13
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/OffsetDateTimePojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.OffsetDateTime;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class OffsetDateTimePojo extends AbstractDateTimePojo<OffsetDateTime> {
+
+    public OffsetDateTimePojo() {
+    }
+
+    public OffsetDateTimePojo(OffsetDateTime date) {
+        super(date);
+        this.offsetDateTime = date;
+    }
+
+    @JsonbDateFormat("Z dd-MM-yyyy--ss:mm:HH")
+    public OffsetDateTime offsetDateTime;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/OffsetTimePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/OffsetTimePojo.java
new file mode 100644
index 0000000..f84014d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/OffsetTimePojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.OffsetTime;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class OffsetTimePojo extends AbstractDateTimePojo<OffsetTime> {
+
+    public OffsetTimePojo() {
+    }
+
+    public OffsetTimePojo(OffsetTime date) {
+        super(date);
+        this.offsetTime = date;
+    }
+
+    @JsonbDateFormat("HH:mm:ssZ")
+    public OffsetTime offsetTime;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/YearMonthPojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/YearMonthPojo.java
new file mode 100644
index 0000000..9c0bafd
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/YearMonthPojo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import java.time.YearMonth;
+import java.util.Objects;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+
+/**
+ * Pojo object of the {@link YearMonth}.
+ */
+public class YearMonthPojo {
+
+    public YearMonth yearMonth;
+
+    @JsonbDateFormat("MM-yyyy")
+    public YearMonth yearMonthWithFormatter;
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        YearMonthPojo that = (YearMonthPojo) o;
+        return Objects.equals(yearMonth, that.yearMonth) && Objects
+                .equals(yearMonthWithFormatter, that.yearMonthWithFormatter);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(yearMonth, yearMonthWithFormatter);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ZonedDateTimePojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ZonedDateTimePojo.java
new file mode 100644
index 0000000..c9c807f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/dates/model/ZonedDateTimePojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.dates.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import java.time.ZonedDateTime;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ZonedDateTimePojo extends AbstractDateTimePojo<ZonedDateTime> {
+
+    public ZonedDateTimePojo() {
+    }
+
+    public ZonedDateTimePojo(ZonedDateTime date) {
+        super(date);
+        this.customZonedDate = date;
+    }
+
+    @JsonbDateFormat("XVV | dd-MM-yyyy--ss:mm:HH")
+    public ZonedDateTime customZonedDate;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/GenericsTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/GenericsTest.java
new file mode 100644
index 0000000..a1bf7f2
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/GenericsTest.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2015, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.TimeZone;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import java.lang.reflect.Field;
+import java.util.Collection;
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.adapters.model.GenericBox;
+import org.eclipse.yasson.defaultmapping.generics.model.AnotherGenericTestClass;
+import org.eclipse.yasson.defaultmapping.generics.model.BoundedGenericClass;
+import org.eclipse.yasson.defaultmapping.generics.model.Circle;
+import org.eclipse.yasson.defaultmapping.generics.model.CollectionWrapper;
+import org.eclipse.yasson.defaultmapping.generics.model.ColoredCircle;
+import org.eclipse.yasson.defaultmapping.generics.model.CyclicSubClass;
+import org.eclipse.yasson.defaultmapping.generics.model.FinalGenericWrapper;
+import org.eclipse.yasson.defaultmapping.generics.model.FinalMember;
+import org.eclipse.yasson.defaultmapping.generics.model.GenericArrayClass;
+import org.eclipse.yasson.defaultmapping.generics.model.GenericTestClass;
+import org.eclipse.yasson.defaultmapping.generics.model.GenericWithUnboundedWildcardClass;
+import org.eclipse.yasson.defaultmapping.generics.model.MultiLevelExtendedGenericTestClass;
+import org.eclipse.yasson.defaultmapping.generics.model.MultipleBoundsContainer;
+import org.eclipse.yasson.defaultmapping.generics.model.MyCyclicGenericClass;
+import org.eclipse.yasson.defaultmapping.generics.model.PropagatedGenericClass;
+import org.eclipse.yasson.defaultmapping.generics.model.Shape;
+import org.eclipse.yasson.defaultmapping.generics.model.WildCardClass;
+import org.eclipse.yasson.defaultmapping.generics.model.WildcardMultipleBoundsClass;
+import org.eclipse.yasson.serializers.model.Box;
+import org.eclipse.yasson.serializers.model.Crate;
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import org.eclipse.yasson.defaultmapping.generics.model.LowerBoundTypeVariableWithCollectionAttributeClass;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * This class contains JSONB default mapping generics tests.
+ *
+ * @author Dmitry Kornilov
+ */
+public class GenericsTest {
+
+    @Test
+    public void testGenericClass() {
+        GenericTestClass<String, Integer> genericClass = new GenericTestClass<>();
+        genericClass.field1 = "value1";
+        genericClass.field2 = 3;
+
+        String expected = "{\"field1\":\"value1\",\"field2\":3}";
+        assertEquals(expected, defaultJsonb.toJson(genericClass));
+
+        Type type = new TestTypeToken<GenericTestClass<String, Integer>>(){}.getType();
+        GenericTestClass<String, Integer> result = defaultJsonb.fromJson(expected, type);
+        assertEquals("value1", result.field1);
+        assertEquals(Integer.valueOf(3), result.field2);
+    }
+
+    @Test
+    public void testMultiLevelGenericClass() {
+
+        GenericTestClass<String, Integer> innerMostGenericClass = new GenericTestClass<>();
+        innerMostGenericClass.field1 = "innerMostValue3";
+        innerMostGenericClass.field2 = 3;
+
+        AnotherGenericTestClass<GenericTestClass<String, Integer>, Integer> another = new AnotherGenericTestClass<>();
+        another.field1 = innerMostGenericClass;
+        another.field2 = 2;
+
+        GenericTestClass<String, AnotherGenericTestClass<GenericTestClass<String, Integer>, Integer>> nestedGenericField = new GenericTestClass<>();
+        nestedGenericField.field1 = "outerValue1";
+        nestedGenericField.field2 = another;
+
+        String expected = "{\"field1\":\"outerValue1\",\"field2\":{\"field1\":{\"field1\":\"innerMostValue3\",\"field2\":3},\"field2\":2}}";
+        assertEquals(expected, defaultJsonb.toJson(nestedGenericField));
+
+        Type type = new TestTypeToken<GenericTestClass<String, AnotherGenericTestClass<GenericTestClass<String, Integer>, Integer>>>(){}.getType();
+        GenericTestClass<String, AnotherGenericTestClass<GenericTestClass<String, Integer>, Integer>> result = defaultJsonb.fromJson(expected, type);
+        assertEquals("outerValue1", result.field1);
+        assertEquals(Integer.valueOf(2), result.field2.field2);
+        assertEquals("innerMostValue3", result.field2.field1.field1);
+        assertEquals(Integer.valueOf(3), result.field2.field1.field2);
+    }
+
+    @Test
+    public void testNestedGenericSelfClass() {
+        GenericTestClass<String, Integer> inner = new GenericTestClass<>();
+        inner.field1 = "innerValue1";
+        inner.field2 = 5;
+
+        GenericTestClass<String, GenericTestClass<String, Integer>> nestedGenericOnSelfClass = new GenericTestClass<>();
+        nestedGenericOnSelfClass.field1 = "outerValue1";
+        nestedGenericOnSelfClass.field2 = inner;
+
+        String expected = "{\"field1\":\"outerValue1\",\"field2\":{\"field1\":\"innerValue1\",\"field2\":5}}";
+        assertEquals(expected, defaultJsonb.toJson(nestedGenericOnSelfClass));
+
+        Type type = new TestTypeToken<GenericTestClass<String, GenericTestClass<String, Integer>>>(){}.getType();
+        GenericTestClass<String, GenericTestClass<String, Integer>> result = defaultJsonb.fromJson(expected, type);
+        assertEquals("outerValue1", result.field1);
+        assertEquals("innerValue1", result.field2.field1);
+        assertEquals(Integer.valueOf(5), result.field2.field2);
+    }
+
+    @Test
+    public void testCyclicGenericClass() {
+
+        final MyCyclicGenericClass<CyclicSubClass> myCyclicGenericClass = new MyCyclicGenericClass<>();
+        final CyclicSubClass cyclicSubClass = new CyclicSubClass();
+        cyclicSubClass.subField = "subFieldValue";
+        myCyclicGenericClass.field1 = cyclicSubClass;
+
+        String expected = "{\"field1\":{\"subField\":\"subFieldValue\"}}";
+        assertEquals(expected, defaultJsonb.toJson(myCyclicGenericClass));
+        MyCyclicGenericClass<CyclicSubClass> result = defaultJsonb.fromJson(expected, new TestTypeToken<MyCyclicGenericClass<CyclicSubClass>>(){}.getType());
+        assertEquals(CyclicSubClass.class, result.field1.getClass());
+        assertEquals("subFieldValue", result.field1.subField);
+    }
+
+    @Test
+    public void testWildCards() {
+
+        final WildCardClass<Integer> integerWildCard = new WildCardClass<>();
+
+        integerWildCard.number = 10;
+        String expected = "{\"number\":10}";
+        assertEquals(expected, defaultJsonb.toJson(integerWildCard));
+        WildCardClass<Integer> result = defaultJsonb.fromJson(expected, new TestTypeToken<WildCardClass<Integer>>(){}.getType());
+        assertEquals(Integer.valueOf(10), result.number);
+    }
+
+    @Test
+    public void testGenericWithUnboundedWildcard() {
+        //wildcardList is treated as List<Object>
+        String expected = "{\"wildcardList\":[{\"k1\":\"v1\",\"k2\":\"v2\"}]}";
+
+        GenericWithUnboundedWildcardClass genericWithUnboundedWildcardClass = new GenericWithUnboundedWildcardClass();
+        List<Map<String, String>> list = new ArrayList<>();
+        genericWithUnboundedWildcardClass.wildcardList = list;
+        Map<String, String> stringMap = new HashMap<>();
+        stringMap.put("k1", "v1");
+        stringMap.put("k2", "v2");
+        list.add(stringMap);
+        assertEquals(expected, defaultJsonb.toJson(genericWithUnboundedWildcardClass));
+
+        GenericWithUnboundedWildcardClass result = defaultJsonb.fromJson(expected, GenericWithUnboundedWildcardClass.class);
+        assertTrue(result.wildcardList.get(0) instanceof Map);
+        assertEquals("v1", ((Map) result.wildcardList.get(0)).get("k1"));
+        assertEquals("v2", ((Map) result.wildcardList.get(0)).get("k2"));
+    }
+
+    @Test
+    public void testWildCardMultipleBounds() {
+
+        WildcardMultipleBoundsClass<BigDecimal> multipleBoundsClass = new WildcardMultipleBoundsClass<>();
+        multipleBoundsClass.wildcardField = BigDecimal.ONE;
+
+        GenericTestClass<String, BigDecimal> genericTestClass = new GenericTestClass<>();
+        genericTestClass.field1 = "genericTestClassField1";
+        genericTestClass.field2 = BigDecimal.TEN;
+        multipleBoundsClass.genericTestClassPropagatedWildCard = genericTestClass;
+
+        List<ExtendsBigDecimal> extendsBigDecimalList = new ArrayList<>();
+        extendsBigDecimalList.add(new ExtendsBigDecimal("11"));
+        multipleBoundsClass.propagatedWildcardList = extendsBigDecimalList;
+
+        String expected = "{\"genericTestClassPropagatedWildCard\":{\"field1\":\"genericTestClassField1\",\"field2\":10},\"propagatedWildcardList\":[11],\"wildcardField\":1}";
+        assertEquals(expected, defaultJsonb.toJson(multipleBoundsClass, new WildcardMultipleBoundsClass<BigDecimal>(){}.getClass()));
+
+
+        WildcardMultipleBoundsClass<BigDecimal> result = defaultJsonb.fromJson(expected, new TestTypeToken<WildcardMultipleBoundsClass<BigDecimal>>(){}.getType());
+        assertEquals(BigDecimal.ONE, result.wildcardField);
+        assertEquals("genericTestClassField1", result.genericTestClassPropagatedWildCard.field1);
+        assertEquals(BigDecimal.TEN, result.genericTestClassPropagatedWildCard.field2);
+        assertEquals(new BigDecimal("11"), result.propagatedWildcardList.get(0));
+
+    }
+
+    @Test
+    public void testWithType() {
+        List<Optional<String>> expected = Arrays.asList(Optional.empty(), Optional.of("first"), Optional.of("second"));
+        //String json = jsonb.toJson(expected, DefaultMappingGenericsTest.class.getField("listOfOptionalStringField").getGenericType());
+
+        String json = defaultJsonb.toJson(expected);
+        assertEquals("[null,\"first\",\"second\"]", json);
+    }
+
+    @Test
+    public void testPropagatedGenerics() {
+        List<Integer> integerList = new ArrayList<>();
+        integerList.add(1);
+        integerList.add(2);
+
+        GenericTestClass<List<Integer>, String> genericInList = new GenericTestClass<>();
+        genericInList.field1 = integerList;
+        genericInList.field2 = "GenericsInListF2";
+
+        List<GenericTestClass<List<Integer>, String>> listWithGenerics = new ArrayList<>();
+        listWithGenerics.add(genericInList);
+
+        GenericTestClass<Integer, String> genericTestClass = new GenericTestClass<>();
+        genericTestClass.field1 = 1;
+        genericTestClass.field2 = "first";
+
+        PropagatedGenericClass<Integer, String> propagatedGenericClass = new PropagatedGenericClass<>();
+        propagatedGenericClass.genericTestClass = genericTestClass;
+        propagatedGenericClass.genericList = listWithGenerics;
+
+        String expected = "{\"genericList\":[{\"field1\":[1,2],\"field2\":\"GenericsInListF2\"}],\"genericTestClass\":{\"field1\":1,\"field2\":\"first\"}}";
+
+        assertEquals(expected, defaultJsonb.toJson(propagatedGenericClass, new TestTypeToken<PropagatedGenericClass<Integer, String>>(){}.getType()));
+        PropagatedGenericClass<Integer, String> result = defaultJsonb.fromJson(expected, new TestTypeToken<PropagatedGenericClass<Integer, String>>(){}.getType());
+        assertEquals(GenericTestClass.class, result.genericList.get(0).getClass());
+        assertEquals(Integer.valueOf(1), result.genericList.get(0).field1.get(0));
+        assertEquals(Integer.valueOf(2), result.genericList.get(0).field1.get(1));
+        assertEquals("GenericsInListF2", result.genericList.get(0).field2);
+        assertEquals(GenericTestClass.class, result.genericTestClass.getClass());
+        assertEquals(Integer.valueOf(1), result.genericTestClass.field1);
+        assertEquals("first", result.genericTestClass.field2);
+
+    }
+
+    @Test
+    public void testMarshallPropagatedGenericsRaw() {
+
+        List<Integer> integerList = new ArrayList<>();
+        integerList.add(1);
+        integerList.add(2);
+
+        GenericBox<List<Integer>> box = new GenericBox<>();
+        box.setX(integerList);
+        box.setStrField("IntegerListBox");
+
+        GenericTestClass<GenericBox<List<Integer>>, String> pojo = new GenericTestClass<>();
+        pojo.field1 = box;
+        pojo.field2 = "GenericTestClass";
+
+        assertEquals("{\"field1\":{\"strField\":\"IntegerListBox\",\"x\":[1,2]},\"field2\":\"GenericTestClass\"}",
+                	defaultJsonb.toJson(pojo));
+    }
+
+
+
+    @Test
+    public void testFunctional() {
+        FunctionalInterface myFunction = new FunctionalInterface<String>() {
+
+            private String value = "initValue";
+
+            @Override
+            public String getValue() {
+                return value;
+            }
+
+            public void setValue(String value) {
+                this.value = value;
+            }
+        };
+
+        assertEquals("{\"value\":\"initValue\"}", defaultJsonb.toJson(myFunction));
+    }
+
+    @Test
+    public void testBoundedGenerics() {
+        //bounded generics
+        BoundedGenericClass<HashSet<Integer>, Circle> boundedGenericClass = new BoundedGenericClass<>();
+        List<Shape> shapeList = new ArrayList<>();
+        Circle circle = new Circle();
+        circle.setRadius(2.5);
+        shapeList.add(circle);
+        boundedGenericClass.lowerBoundedList = shapeList;
+
+        List<ColoredCircle> coloredCircles = new ArrayList<>();
+        ColoredCircle coloredCircle = new ColoredCircle();
+        coloredCircle.radius = 3.5;
+        coloredCircle.color = "0,0,255";
+        coloredCircles.add(coloredCircle);
+        boundedGenericClass.upperBoundedList = coloredCircles;
+
+        HashSet<Integer> intSet = new HashSet<>();
+        intSet.add(3);
+        boundedGenericClass.boundedSet = intSet;
+
+        String expected = "{\"boundedSet\":[3],\"lowerBoundedList\":[{\"radius\":2.5}],\"upperBoundedList\":[{\"radius\":3.5,\"color\":\"0,0,255\"}]}";
+        assertEquals(expected, defaultJsonb.toJson(boundedGenericClass));
+
+        Jsonb localJsonb = JsonbBuilder.create(new JsonbConfig());
+        BoundedGenericClass<HashSet<Integer>, Circle> result = localJsonb.fromJson(expected,
+                new TestTypeToken<BoundedGenericClass<HashSet<Integer>, Circle>>(){}.getType());
+        assertEquals(Circle.class, result.lowerBoundedList.get(0).getClass());
+        assertEquals(Double.valueOf(2.5), ((Circle) result.lowerBoundedList.get(0)).getRadius());
+
+        //There is no way of identifying precise class (ColoredCircle) during json unmarshalling.
+        //Fields that are missing in upper bounds are skipped.
+        assertEquals(Circle.class, result.upperBoundedList.get(0).getClass());
+        assertEquals(Double.valueOf(3.5), result.upperBoundedList.get(0).getRadius());
+        //If it was possible we could assert following, but it is not.
+        //assertEquals("0,0,255", ((ColoredCircle) result.upperBoundedList.get(0)).color);
+    }
+
+    @Test
+    public void testIncompatibleTypes() {
+        //exception incompatible types
+    	assertThrows(ClassCastException.class, () -> {
+	        BoundedGenericClass<HashSet<Integer>, Circle> otherGeneric = defaultJsonb.fromJson("{\"boundedSet\":[3],\"lowerBoundedList\":[{\"radius\":2.5}]}",
+	                new TestTypeToken<BoundedGenericClass<HashSet<Double>, Circle>>(){}.getType());
+	        HashSet<Integer> otherIntSet = otherGeneric.boundedSet;
+	        Integer intValue = otherIntSet.iterator().next();
+    	});
+    }
+
+    @Test
+    public void testMultiLevelGenericExtension() {
+        MultiLevelExtendedGenericTestClass extended = new MultiLevelExtendedGenericTestClass();
+        extended.field1 = "first";
+        extended.field2 = 1;
+
+        String expected = "{\"field1\":\"first\",\"field2\":1}";
+        assertEquals(expected, defaultJsonb.toJson(extended));
+        MultiLevelExtendedGenericTestClass result = defaultJsonb.fromJson(expected, MultiLevelExtendedGenericTestClass.class);
+        assertEquals("first", result.field1);
+        assertEquals(Integer.valueOf(1), result.field2);
+    }
+
+    @Test
+    public void testGenericArray() {
+        GenericArrayClass<Number, Integer> genericArrayClass = new GenericArrayClass<>();
+        Number[] numbers = new Number[2];
+        numbers[0] = 1;
+        numbers[1] = BigDecimal.TEN;
+        genericArrayClass.genericArray = numbers;
+
+        Integer[] integers = new Integer[2];
+        integers[0] = 1;
+        integers[1] = 10;
+        genericArrayClass.anotherGenericArray = integers;
+
+        GenericTestClass<Number[], Integer[]> genericTestClass = new GenericTestClass<>();
+        genericTestClass.field1 = Arrays.copyOf(numbers, numbers.length);
+        genericTestClass.field2 = Arrays.copyOf(integers, numbers.length);
+        genericArrayClass.propagatedGenericArray = genericTestClass;
+
+        String expected = "{\"anotherGenericArray\":[1,10],\"genericArray\":[1,10],\"propagatedGenericArray\":{\"field1\":[1,10],\"field2\":[1,10]}}";
+        assertEquals(expected, defaultJsonb.toJson(genericArrayClass, new TestTypeToken<GenericArrayClass<Number,Integer>>(){}.getType()));
+        GenericArrayClass<Number, Integer> result = defaultJsonb.fromJson(expected, new TestTypeToken<GenericArrayClass<Number, Integer>>(){}.getType());
+        assertEquals(BigDecimal.ONE, result.genericArray[0]);
+        assertEquals(BigDecimal.TEN, result.genericArray[1]);
+        assertEquals(Integer.valueOf(1), result.anotherGenericArray[0]);
+        assertEquals(Integer.valueOf(10), result.anotherGenericArray[1]);
+        assertEquals(BigDecimal.ONE, result.propagatedGenericArray.field1[0]);
+        assertEquals(BigDecimal.TEN, result.propagatedGenericArray.field1[1]);
+        assertEquals(Integer.valueOf(1), result.propagatedGenericArray.field2[0]);
+        assertEquals(Integer.valueOf(10), result.propagatedGenericArray.field2[1]);
+
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testMarshallRawList() throws ParseException {
+        List rawList = new ArrayList();
+        final SimpleDateFormat ddMMyyyy = new SimpleDateFormat("ddMMyyyy");
+        ddMMyyyy.setTimeZone(TimeZone.getTimeZone("Z"));
+        rawList.add(ddMMyyyy.parse("24031981"));
+        Box box = new Box();
+        box.boxStr = "box string";
+        box.crate = new Crate();
+        box.crate.crateStr = "crate str";
+        rawList.add(box);
+
+        String result = defaultJsonb.toJson(rawList);
+        assertEquals("[\"1981-03-24T00:00:00Z[UTC]\",{\"boxStr\":\"box string\",\"crate\":{\"crate_str\":\"crate str\"}}]", result);
+    }
+
+    @Test
+    public void testMultipleBounds() {
+        final LinkedList<String> list = new LinkedList<>(Arrays.asList("Test 1", "Test 2"));
+        MultipleBoundsContainer<LinkedList<String>> container = new MultipleBoundsContainer<>();
+        container.setInstance(new ArrayList<>());
+        container.getInstance().add(list);
+
+        final Type type = new TestTypeToken<MultipleBoundsContainer<LinkedList<String>>>() {
+        }.getType();
+
+        String jsonString = defaultJsonb.toJson(container, type);
+        assertEquals("{\"instance\":[[\"Test 1\",\"Test 2\"]]}", jsonString);
+
+        MultipleBoundsContainer<LinkedList<String>> result = defaultJsonb.fromJson(jsonString, type);
+
+        assertEquals(container.getInstance(), result.getInstance());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testDeserializeIntoRaw() {
+
+        GenericTestClass result = defaultJsonb.fromJson("{\"field1\":{\"val1\":\"abc\"},\"field2\":{\"val1\":\"def\"}}", GenericTestClass.class);
+        assertEquals(((HashMap<String, ?>) result.getField1()).get("val1"), "abc");
+        assertEquals(((HashMap<String, ?>) result.getField2()).get("val1"), "def");
+    }
+
+    @Test
+    public void collectionWrapperTest() {
+        CollectionWrapper<String> collectionWrapper = new CollectionWrapper<>();
+        collectionWrapper.setCollection(new ArrayList<>());
+
+        collectionWrapper.setWrappedCollection(new ArrayList<>());
+        collectionWrapper.setWrappedMap(new HashMap<>());
+        String s = defaultJsonb.toJson(collectionWrapper);
+    }
+
+    @Test
+    public void multipleGenericLevels() {
+        FinalMember member = new FinalMember();
+        member.setName("Jason");
+        FinalGenericWrapper concreteContainer = new FinalGenericWrapper();
+        concreteContainer.setMember(member);
+
+        String expected = "{\"member\":{\"name\":\"Jason\"}}";
+        Jsonb jsonb = JsonbBuilder.create();
+        assertEquals(expected, jsonb.toJson(concreteContainer));
+        FinalGenericWrapper finalGenericWrapper = jsonb.fromJson(expected, FinalGenericWrapper.class);
+        assertEquals(concreteContainer, finalGenericWrapper);
+    }
+
+    @Test
+    public void lowerBoundTypeVariableInCollectionAttribute() throws Exception {
+        
+        Shape shape = new Shape();
+        shape.setArea(5D);
+        
+        AnotherGenericTestClass<Integer, Shape> anotherGenericTestClass = new AnotherGenericTestClass<>();
+        anotherGenericTestClass.field1 = 6;
+        anotherGenericTestClass.field2 = shape;
+        
+        List<AnotherGenericTestClass<Integer, Shape>> asList = Arrays.asList(anotherGenericTestClass);
+        
+        Jsonb jsonb = JsonbBuilder.create();
+        String toJson = jsonb.toJson(asList);
+        
+        Field field = LowerBoundTypeVariableWithCollectionAttributeClass.class.getDeclaredField("value");
+        
+        Type genericType = field.getGenericType();
+        
+        List<AnotherGenericTestClass<Integer, Shape>> fromJson = jsonb.fromJson(toJson, genericType);
+
+        assertEquals(5, fromJson.get(0).field2.getArea());
+        assertEquals(6, fromJson.get(0).field1);
+        
+    }
+    
+    public interface FunctionalInterface<T> {
+        T getValue();
+    }
+
+    public static class ExtendsBigDecimal extends BigDecimal {
+
+        public ExtendsBigDecimal(String val) {
+            super(val);
+        }
+    }
+
+}
+
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AbstractGenericWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AbstractGenericWrapper.java
new file mode 100644
index 0000000..61149c5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AbstractGenericWrapper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.util.Objects;
+
+public abstract class AbstractGenericWrapper<T extends AbstractMember> {
+
+    T member;
+
+    public T getMember() {
+        return member;
+    }
+
+    public void setMember(T member) {
+        this.member = member;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        AbstractGenericWrapper<?> that = (AbstractGenericWrapper<?>) o;
+        return Objects.equals(member, that.member);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(member);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AbstractMember.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AbstractMember.java
new file mode 100644
index 0000000..534109f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AbstractMember.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+public class AbstractMember {
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AnotherGenericTestClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AnotherGenericTestClass.java
new file mode 100644
index 0000000..894ef5c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/AnotherGenericTestClass.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class AnotherGenericTestClass<T1, T2> {
+
+    public T1 field1;
+
+    public T2 field2;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/BoundedGenericClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/BoundedGenericClass.java
new file mode 100644
index 0000000..24c9713
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/BoundedGenericClass.java
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class BoundedGenericClass<T extends Set<? extends Number>, U> {
+    public List<? extends U> upperBoundedList;
+    public List<? super U> lowerBoundedList;
+    public T boundedSet;
+
+    public BoundedGenericClass() {
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/Circle.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/Circle.java
new file mode 100644
index 0000000..585e4fc
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/Circle.java
@@ -0,0 +1,32 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class Circle extends Shape {
+    public Double radius;
+
+    public Circle() {
+        super();
+    }
+
+    public Double getRadius() {
+        return radius;
+    }
+
+    public void setRadius(double radius) {
+        this.radius = radius;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CollectionWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CollectionWrapper.java
new file mode 100644
index 0000000..2ddc79f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CollectionWrapper.java
@@ -0,0 +1,52 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.util.Collection;
+import java.util.Map;
+
+public class CollectionWrapper<T> {
+
+    public CollectionWrapper() {
+    }
+
+    private Collection<T> collection;
+
+    private Collection<Collection<T>> wrappedCollection;
+
+    private Map<String, Map<String,String>> wrappedMap;
+
+    public Collection<T> getCollection() {
+        return collection;
+    }
+
+    public void setCollection(Collection<T> collection) {
+        this.collection = collection;
+    }
+
+    public Collection<Collection<T>> getWrappedCollection() {
+        return wrappedCollection;
+    }
+
+    public void setWrappedCollection(Collection<Collection<T>> wrappedCollection) {
+        this.wrappedCollection = wrappedCollection;
+    }
+
+    public Map<String, Map<String, String>> getWrappedMap() {
+        return wrappedMap;
+    }
+
+    public void setWrappedMap(Map<String, Map<String, String>> wrappedMap) {
+        this.wrappedMap = wrappedMap;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ColoredCircle.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ColoredCircle.java
new file mode 100644
index 0000000..0bedfb7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ColoredCircle.java
@@ -0,0 +1,21 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ColoredCircle extends Circle {
+
+    public String color;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CyclicSubClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CyclicSubClass.java
new file mode 100644
index 0000000..36a7e82
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/CyclicSubClass.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CyclicSubClass extends MyCyclicGenericClass<CyclicSubClass> {
+    public String subField;
+
+    public CyclicSubClass() {
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ExtendedGenericTestClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ExtendedGenericTestClass.java
new file mode 100644
index 0000000..9dcb547
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ExtendedGenericTestClass.java
@@ -0,0 +1,19 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ExtendedGenericTestClass extends GenericTestClass<String, Integer> {
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/FinalGenericWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/FinalGenericWrapper.java
new file mode 100644
index 0000000..12bec00
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/FinalGenericWrapper.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+public class FinalGenericWrapper extends MiddleGenericWrapper<FinalMember> {
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/FinalMember.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/FinalMember.java
new file mode 100644
index 0000000..47ee0fd
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/FinalMember.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.util.Objects;
+
+public class FinalMember extends AbstractMember {
+    String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        FinalMember that = (FinalMember) o;
+        return Objects.equals(name, that.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericArrayClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericArrayClass.java
new file mode 100644
index 0000000..46fc040
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericArrayClass.java
@@ -0,0 +1,22 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class GenericArrayClass<T, U extends T> {
+    public T[] genericArray;
+    public U[] anotherGenericArray;
+    public GenericTestClass<T[], U[]> propagatedGenericArray;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericTestClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericTestClass.java
new file mode 100644
index 0000000..7684284
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericTestClass.java
@@ -0,0 +1,41 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * Test class used in generics tests.
+ *
+ * @author Dmitry Kornilov
+ */
+public class GenericTestClass<T,U> {
+    public T field1;
+    public U field2;
+
+    public GenericTestClass() {}
+
+    public T getField1() {
+        return field1;
+    }
+
+    public void setField1(T field1) {
+        this.field1 = field1;
+    }
+
+    public U getField2() {
+        return field2;
+    }
+
+    public void setField2(U field2) {
+        this.field2 = field2;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericWithUnboundedWildcardClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericWithUnboundedWildcardClass.java
new file mode 100644
index 0000000..b33ba15
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/GenericWithUnboundedWildcardClass.java
@@ -0,0 +1,25 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.util.List;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class GenericWithUnboundedWildcardClass {
+    public List<?> wildcardList;
+
+    public GenericWithUnboundedWildcardClass() {
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/LowerBoundTypeVariableWithCollectionAttributeClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/LowerBoundTypeVariableWithCollectionAttributeClass.java
new file mode 100644
index 0000000..7f58792
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/LowerBoundTypeVariableWithCollectionAttributeClass.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.util.Collection;
+
+/**
+ * @author Alessandro Moscatelli
+ */
+public class LowerBoundTypeVariableWithCollectionAttributeClass<T extends Shape> {
+    
+    private Collection<AnotherGenericTestClass<Integer, T>> value;
+
+    public Collection<AnotherGenericTestClass<Integer, T>> getValue() {
+        return value;
+    }
+
+    public void setValue(Collection<AnotherGenericTestClass<Integer, T>> value) {
+        this.value = value;
+    }
+    
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MiddleGenericWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MiddleGenericWrapper.java
new file mode 100644
index 0000000..01ea210
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MiddleGenericWrapper.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+public abstract class MiddleGenericWrapper<T extends AbstractMember> extends AbstractGenericWrapper<T> {
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultiLevelExtendedGenericTestClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultiLevelExtendedGenericTestClass.java
new file mode 100644
index 0000000..3a5f1f6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultiLevelExtendedGenericTestClass.java
@@ -0,0 +1,19 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class MultiLevelExtendedGenericTestClass extends ExtendedGenericTestClass {
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultipleBoundsContainer.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultipleBoundsContainer.java
new file mode 100644
index 0000000..39682cf
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MultipleBoundsContainer.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 Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/*
+ * $Id$
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.util.List;
+import java.util.Queue;
+
+public class MultipleBoundsContainer<T extends List & Queue> implements TypeContainer<List<T>> {
+    protected List<T> instance;
+
+    @Override
+    public List<T> getInstance() {
+        return instance;
+    }
+
+    @Override
+    public void setInstance(List<T> instance) {
+        this.instance = instance;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MyCyclicGenericClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MyCyclicGenericClass.java
new file mode 100644
index 0000000..e396774
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/MyCyclicGenericClass.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class MyCyclicGenericClass<T extends MyCyclicGenericClass<? extends T>> {
+    public T field1;
+
+    public MyCyclicGenericClass() {
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/PropagatedGenericClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/PropagatedGenericClass.java
new file mode 100644
index 0000000..41fc999
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/PropagatedGenericClass.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.util.List;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class PropagatedGenericClass<P, X> {
+    public List<GenericTestClass<List<P>, X>> genericList;
+    public GenericTestClass<P, X> genericTestClass;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ScalarValueWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ScalarValueWrapper.java
new file mode 100644
index 0000000..dd1c1bb
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/ScalarValueWrapper.java
@@ -0,0 +1,36 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ScalarValueWrapper<T> {
+
+    private T value;
+
+    public ScalarValueWrapper() {
+    }
+
+    public ScalarValueWrapper(T value) {
+        this.value = value;
+    }
+
+    public T getValue() {
+        return value;
+    }
+
+    public void setValue(T value) {
+        this.value = value;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/Shape.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/Shape.java
new file mode 100644
index 0000000..fbe628b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/Shape.java
@@ -0,0 +1,31 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class Shape {
+    private Double area;
+
+    public Shape() {
+    }
+
+    public Double getArea() {
+        return area;
+    }
+
+    public void setArea(Double area) {
+        this.area = area;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/TypeContainer.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/TypeContainer.java
new file mode 100644
index 0000000..a9755a8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/TypeContainer.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+public interface TypeContainer<T> {
+    T getInstance();
+    void setInstance(T instance);
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/WildCardClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/WildCardClass.java
new file mode 100644
index 0000000..5638e01
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/WildCardClass.java
@@ -0,0 +1,22 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class WildCardClass <T extends Number> {
+
+    public T number;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/WildcardMultipleBoundsClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/WildcardMultipleBoundsClass.java
new file mode 100644
index 0000000..bc1379d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/generics/model/WildcardMultipleBoundsClass.java
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.generics.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class WildcardMultipleBoundsClass<T extends Number & Serializable & Comparable<? extends T>> {
+
+    public T wildcardField;
+
+    public GenericTestClass<String, T> genericTestClassPropagatedWildCard;
+
+    public List<? extends T> propagatedWildcardList;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/InheritanceTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/InheritanceTest.java
new file mode 100644
index 0000000..9652fe5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/InheritanceTest.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.generics.model.GenericTestClass;
+import org.eclipse.yasson.defaultmapping.generics.model.PropagatedGenericClass;
+import org.eclipse.yasson.defaultmapping.inheritance.model.PartialOverride;
+import org.eclipse.yasson.defaultmapping.inheritance.model.PropertyOrderSecond;
+import org.eclipse.yasson.defaultmapping.inheritance.model.SecondLevel;
+import org.eclipse.yasson.defaultmapping.inheritance.model.generics.ExtendsExtendsPropagatedGenericClass;
+import org.eclipse.yasson.defaultmapping.inheritance.model.generics.ExtendsPropagatedGenericClass;
+import org.eclipse.yasson.defaultmapping.inheritance.model.generics.ImplementsGenericInterfaces;
+import org.eclipse.yasson.defaultmapping.inheritance.model.generics.SecondLevelGeneric;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests inheritance model marshalling / unmarshalling
+ *
+ * Tests property order, method overloading, generic type ({@link java.lang.reflect.TypeVariable}) resolving for unmarshalling.
+ *
+ * @author Roman Grigoriadi
+ */
+public class InheritanceTest {
+
+    @Test
+    public void testBasicInheritance() throws Exception {
+        SecondLevel secondLevel = new SecondLevel();
+        secondLevel.setInFirstLevel("IN_FIRST_LEVEL");
+        secondLevel.setInSecondLevel("IN_SECOND_LEVEL");
+        secondLevel.setInZeroOverriddenInFirst("IN_ZERO_OVERRIDDEN_IN_FIRST");
+
+        String json = "{\"inZeroOverriddenInFirst\":\"IN_ZERO_OVERRIDDEN_IN_FIRST\",\"inFirstLevel\":\"IN_FIRST_LEVEL\",\"inSecondLevel\":\"IN_SECOND_LEVEL\"}";
+        assertEquals(json, defaultJsonb.toJson(secondLevel));
+
+        SecondLevel result = defaultJsonb.fromJson(json, SecondLevel.class);
+        assertEquals("IN_FIRST_LEVEL", result.getInFirstLevel());
+        assertEquals("IN_SECOND_LEVEL", result.getInSecondLevel());
+        assertEquals("IN_ZERO_OVERRIDDEN_IN_FIRST", result.getInZeroOverriddenInFirst());
+
+    }
+
+    @Test
+    public void testBasicGenericInheritance() {
+        SecondLevelGeneric<Number, Short, String> secondLevelGeneric = new SecondLevelGeneric<>();
+        secondLevelGeneric.setInSecondLevel(BigDecimal.TEN);
+        secondLevelGeneric.setInFirstLevel((short) 255);
+        secondLevelGeneric.setInZeroOverriddenInFirst("IN_ZERO_OVERRIDDEN_IN_FIRST");
+        secondLevelGeneric.setInZero("IN_ZERO");
+
+        String json = "{\"inZero\":\"IN_ZERO\",\"inFirstLevel\":255,\"inZeroOverriddenInFirst\":\"IN_ZERO_OVERRIDDEN_IN_FIRST\",\"inSecondLevel\":10}";
+        assertEquals(json, defaultJsonb.toJson(secondLevelGeneric));
+
+        SecondLevelGeneric<Number, Short, String> result = defaultJsonb.fromJson(json, new TestTypeToken<SecondLevelGeneric<Number, Short, String>>(){}.getType());
+        assertEquals(BigDecimal.TEN, result.getInSecondLevel());
+        assertEquals(Short.valueOf("255"), result.getInFirstLevel());
+        assertEquals("IN_ZERO_OVERRIDDEN_IN_FIRST", result.getInZeroOverriddenInFirst());
+        assertEquals("IN_ZERO", result.getInZero());
+    }
+
+    @Test
+    public void testPropagatedGenericInheritance() throws Exception {
+        List<String> stringList = new ArrayList<>();
+        stringList.add("first");
+        stringList.add("second");
+
+        GenericTestClass<List<String>, BigDecimal> genericInList = new GenericTestClass<>();
+        genericInList.field1 = stringList;
+        genericInList.field2 = BigDecimal.TEN;
+
+        List<GenericTestClass<List<String>, BigDecimal>> listWithGenerics = new ArrayList<>();
+        listWithGenerics.add(genericInList);
+
+        GenericTestClass<String, BigDecimal> genericTestClass = new GenericTestClass<>();
+        genericTestClass.field1 = "GENERIC_STRING";
+        genericTestClass.field2 = BigDecimal.ONE;
+
+        ExtendsExtendsPropagatedGenericClass underTest = new ExtendsExtendsPropagatedGenericClass();
+        underTest.genericTestClass = genericTestClass;
+        underTest.genericList = listWithGenerics;
+
+        String json = "{\"genericList\":[{\"field1\":[\"first\",\"second\"],\"field2\":10}],\"genericTestClass\":{\"field1\":\"GENERIC_STRING\",\"field2\":1}}";
+        assertEquals(json, defaultJsonb.toJson(underTest));
+
+        ExtendsExtendsPropagatedGenericClass result = defaultJsonb.fromJson(json, ExtendsExtendsPropagatedGenericClass.class);
+        assertEquals(GenericTestClass.class, result.genericList.get(0).getClass());
+        assertEquals("first", result.genericList.get(0).field1.get(0));
+        assertEquals("second", result.genericList.get(0).field1.get(1));
+        assertEquals(BigDecimal.TEN, result.genericList.get(0).field2);
+        assertEquals(GenericTestClass.class, result.genericTestClass.getClass());
+        assertEquals("GENERIC_STRING", result.genericTestClass.field1);
+        assertEquals(BigDecimal.ONE, result.genericTestClass.field2);
+    }
+
+    @Test
+    public void testPropagatedGenericInheritance1() throws Exception {
+        List<String> stringList = new ArrayList<>();
+        stringList.add("first");
+        stringList.add("second");
+
+        GenericTestClass<List<String>, BigDecimal> genericInList = new GenericTestClass<>();
+        genericInList.field1 = stringList;
+        genericInList.field2 = BigDecimal.TEN;
+
+        List<GenericTestClass<List<String>, BigDecimal>> listWithGenerics = new ArrayList<>();
+        listWithGenerics.add(genericInList);
+
+        GenericTestClass<String, BigDecimal> genericTestClass = new GenericTestClass<>();
+        genericTestClass.field1 = "SECOND_LEVEL_GENERIC_STRING";
+        genericTestClass.field2 = BigDecimal.ONE;
+
+        PropagatedGenericClass<String, BigDecimal> propagatedGenericClass = new PropagatedGenericClass<>();
+        propagatedGenericClass.genericList = listWithGenerics;
+        propagatedGenericClass.genericTestClass = genericTestClass;
+
+
+        stringList = new ArrayList<>();
+        stringList.add("third");
+        stringList.add("fourth");
+
+        genericInList = new GenericTestClass<>();
+        genericInList.field1 = stringList;
+        genericInList.field2 = BigDecimal.ZERO;
+
+        listWithGenerics = new ArrayList<>();
+        listWithGenerics.add(genericInList);
+
+        genericTestClass = new GenericTestClass<>();
+        genericTestClass.field1 = "FIRST_LEVEL_GENERIC_STRING";
+        genericTestClass.field2 = new BigDecimal("11");
+
+        ExtendsPropagatedGenericClass<String, BigDecimal> extendsPropagatedGenericClass = new ExtendsPropagatedGenericClass<>();
+        extendsPropagatedGenericClass.genericList = listWithGenerics;
+        extendsPropagatedGenericClass.genericTestClass = genericTestClass;
+
+        SecondLevelGeneric<PropagatedGenericClass<String, BigDecimal>, ExtendsPropagatedGenericClass<String, BigDecimal>, String> secondLevelGeneric = new SecondLevelGeneric<>();
+        secondLevelGeneric.setInSecondLevel(propagatedGenericClass);
+        secondLevelGeneric.setInFirstLevel(extendsPropagatedGenericClass);
+        secondLevelGeneric.setInZeroOverriddenInFirst("STRING_IN_ZERO_OVERRIDDEN_IN_FIRST");
+        secondLevelGeneric.setInZero("IN_ZERO");
+
+        String json = "{\"inZero\":\"IN_ZERO\",\"inFirstLevel\":{\"genericList\":[{\"field1\":[\"third\",\"fourth\"],\"field2\":0}],\"genericTestClass\":{\"field1\":\"FIRST_LEVEL_GENERIC_STRING\",\"field2\":11}},\"inZeroOverriddenInFirst\":\"STRING_IN_ZERO_OVERRIDDEN_IN_FIRST\",\"inSecondLevel\":{\"genericList\":[{\"field1\":[\"first\",\"second\"],\"field2\":10}],\"genericTestClass\":{\"field1\":\"SECOND_LEVEL_GENERIC_STRING\",\"field2\":1}}}";
+
+        final Type runtimeType = new TestTypeToken<SecondLevelGeneric<PropagatedGenericClass<String, BigDecimal>, ExtendsPropagatedGenericClass<String, BigDecimal>, String>>(){}.getType();
+        assertEquals(json, defaultJsonb.toJson(secondLevelGeneric, runtimeType));
+        SecondLevelGeneric<PropagatedGenericClass<String, BigDecimal>, ExtendsPropagatedGenericClass<String, BigDecimal>, String> result =
+        		defaultJsonb.fromJson(json, runtimeType);
+
+        assertEquals("first", result.getInSecondLevel().genericList.get(0).field1.get(0));
+        assertEquals("second", result.getInSecondLevel().genericList.get(0).field1.get(1));
+        assertEquals(BigDecimal.TEN, result.getInSecondLevel().genericList.get(0).field2);
+        assertEquals("SECOND_LEVEL_GENERIC_STRING", result.getInSecondLevel().genericTestClass.field1);
+        assertEquals(BigDecimal.ONE, result.getInSecondLevel().genericTestClass.field2);
+
+        assertEquals("third", result.getInFirstLevel().genericList.get(0).field1.get(0));
+        assertEquals("fourth", result.getInFirstLevel().genericList.get(0).field1.get(1));
+        assertEquals(BigDecimal.ZERO, result.getInFirstLevel().genericList.get(0).field2);
+        assertEquals("FIRST_LEVEL_GENERIC_STRING", result.getInFirstLevel().genericTestClass.field1);
+        assertEquals(new BigDecimal("11"), result.getInFirstLevel().genericTestClass.field2);
+
+        assertEquals("STRING_IN_ZERO_OVERRIDDEN_IN_FIRST", result.getInZeroOverriddenInFirst());
+        assertEquals("IN_ZERO", result.getInZero());
+    }
+
+    @Test
+    public void testInterfaceGenericInheritance() throws Exception {
+        ImplementsGenericInterfaces<String, Integer> implementsGenericInterfaces = new ImplementsGenericInterfaces<>();
+
+        implementsGenericInterfaces.setGenericValue("GENERIC_VALUE");
+        implementsGenericInterfaces.setAnotherGenericValue(255);
+
+        String json = "{\"anotherGenericValue\":255,\"genericValue\":\"GENERIC_VALUE\"}";
+        assertEquals(json, defaultJsonb.toJson(implementsGenericInterfaces));
+
+        ImplementsGenericInterfaces<String, Integer> result = defaultJsonb.fromJson(json, new TestTypeToken<ImplementsGenericInterfaces<String, Integer>>(){}.getType());
+        assertEquals("GENERIC_VALUE", result.getGenericValue());
+        assertEquals(Integer.valueOf(255), result.getAnotherGenericValue());
+    }
+
+    @Test
+    public void testPartialOverride() {
+        PartialOverride partialOverride = new PartialOverride();
+        partialOverride.setIntValue(5);
+        partialOverride.setStrValue("abc");
+        String json = defaultJsonb.toJson(partialOverride);
+        assertEquals("{\"intValue\":5,\"strValue\":\"abc\"}", json);
+
+        PartialOverride result = defaultJsonb.fromJson("{\"intValue\":5,\"strValue\":\"abc\"}", PartialOverride.class);
+        assertEquals(5, result.getIntValue());
+        assertEquals("abc", result.getStrValue());
+    }
+
+    @Test
+    public void testPropOrderPartiallyOverriddenProperty() {
+        PropertyOrderSecond pojo = new PropertyOrderSecond();
+        pojo.setZero("ZERO");
+        pojo.setZeroPartiallyOverriddenInFirst("ZERO_PARTIALLY_OVERRIDDEN_IN_FIRST");
+        pojo.setZeroOverriddenInSecond("ZERO_OVERRIDDEN_IN_SECOND");
+        pojo.setFirst("FIRST");
+        pojo.setSecond("SECOND");
+
+        String result = defaultJsonb.toJson(pojo);
+        assertEquals("{\"zero\":\"ZERO\",\"zeroPartiallyOverriddenInFirst\":\"ZERO_PARTIALLY_OVERRIDDEN_IN_FIRST\",\"first\":\"FIRST\",\"second\":\"SECOND\",\"zeroOverriddenInSecond\":\"ZERO_OVERRIDDEN_IN_SECOND\"}",
+                result);
+    }
+
+    @Test
+    public void testInheritanceSerialization() {
+        AnimalWrapper animalWrapper = new AnimalWrapper();
+        animalWrapper.animal = new Dog();
+        //Just initialize serializer cache for Animal and Dog
+        defaultJsonb.toJson(animalWrapper);
+
+        //Check if the Dog instance is dynamically resolved even though Dog serializer has been created before
+        DogWrapper dogWrapper = new DogWrapper();
+        dogWrapper.dog = new Dog();
+        assertEquals("{\"dog\":{\"isDog\":true}}", defaultJsonb.toJson(dogWrapper));
+        dogWrapper.dog = new SmallDog();
+        assertEquals("{\"dog\":{\"isDog\":true,\"isSmallDog\":true}}", defaultJsonb.toJson(dogWrapper));
+
+    }
+
+    public static class AnimalWrapper {
+
+        public Animal animal;
+
+    }
+
+    public static class DogWrapper {
+
+        public Dog dog;
+
+    }
+
+    public static class Animal {
+
+    }
+
+    public static class Dog extends Animal {
+        public boolean isDog = true;
+    }
+
+    public static class SmallDog extends Dog {
+        public boolean isSmallDog = true;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/AbstractZeroLevel.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/AbstractZeroLevel.java
new file mode 100644
index 0000000..6c9461e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/AbstractZeroLevel.java
@@ -0,0 +1,26 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public abstract class AbstractZeroLevel {
+
+    protected String inZeroOverriddenInFirst;
+
+    abstract String getInZeroOverriddenInFirst();
+
+    abstract void setInZeroOverriddenInFirst(String value);
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/FirstLevel.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/FirstLevel.java
new file mode 100644
index 0000000..fb86e0f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/FirstLevel.java
@@ -0,0 +1,39 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class FirstLevel extends AbstractZeroLevel {
+
+    private String inFirstLevel;
+
+    @Override
+    public String getInZeroOverriddenInFirst() {
+        return inZeroOverriddenInFirst;
+    }
+
+    @Override
+    public void setInZeroOverriddenInFirst(String value) {
+        inZeroOverriddenInFirst = value;
+    }
+
+    public String getInFirstLevel() {
+        return inFirstLevel;
+    }
+
+    public void setInFirstLevel(String inFirstLevel) {
+        this.inFirstLevel = inFirstLevel;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PartialOverride.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PartialOverride.java
new file mode 100644
index 0000000..69cceac
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PartialOverride.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model;
+
+/**
+ * Bias property is not readable by putting field and getter in other class than setter.
+ *
+ * @author Roman Grigoriadi
+ */
+public class PartialOverride extends PartialOverrideBase {
+
+    @Override
+    public void setIntValue(int intValue) {
+        super.setIntValue(intValue);
+    }
+
+    @Override
+    public void setStrValue(String strValue) {
+        super.setStrValue(strValue);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PartialOverrideBase.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PartialOverrideBase.java
new file mode 100644
index 0000000..5be6337
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PartialOverrideBase.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class PartialOverrideBase {
+
+    private int intValue;
+    private String strValue;
+
+    public int getIntValue() {
+        return intValue;
+    }
+
+    public void setIntValue(int intValue) {
+        this.intValue = intValue;
+    }
+
+    public String getStrValue() {
+        return strValue;
+    }
+
+    public void setStrValue(String strValue) {
+        this.strValue = strValue;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderFirst.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderFirst.java
new file mode 100644
index 0000000..8ed4bed
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderFirst.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class PropertyOrderFirst extends PropertyOrderZero {
+
+    private String first;
+
+    public String getFirst() {
+        return first;
+    }
+
+    public void setFirst(String first) {
+        this.first = first;
+    }
+
+    @Override
+    public void setZeroPartiallyOverriddenInFirst(String zeroPartiallyOverriddenInFirst) {
+        super.setZeroPartiallyOverriddenInFirst(zeroPartiallyOverriddenInFirst);
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderSecond.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderSecond.java
new file mode 100644
index 0000000..761593c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderSecond.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class PropertyOrderSecond extends PropertyOrderFirst {
+
+    private String zeroOverridden;
+
+    private String second;
+
+    public String getSecond() {
+        return second;
+    }
+
+    public void setSecond(String second) {
+        this.second = second;
+    }
+
+    @Override
+    public String getZeroOverriddenInSecond() {
+        return zeroOverridden;
+    }
+
+    @Override
+    public void setZeroOverriddenInSecond(String zeroOverriddenInSecond) {
+        this.zeroOverridden = zeroOverriddenInSecond;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderZero.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderZero.java
new file mode 100644
index 0000000..48a477e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/PropertyOrderZero.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class PropertyOrderZero {
+
+    private String zeroOverriddenInSecond;
+
+    //only setter is overridden
+    private String zeroPartiallyOverriddenInFirst;
+
+    private String zero;
+
+    public String getZero() {
+        return zero;
+    }
+
+    public void setZero(String zero) {
+        this.zero = zero;
+    }
+
+    public String getZeroOverriddenInSecond() {
+        return zeroOverriddenInSecond;
+    }
+
+    public void setZeroOverriddenInSecond(String zeroOverriddenInSecond) {
+        this.zeroOverriddenInSecond = zeroOverriddenInSecond;
+    }
+
+    public String getZeroPartiallyOverriddenInFirst() {
+        return zeroPartiallyOverriddenInFirst;
+    }
+
+    public void setZeroPartiallyOverriddenInFirst(String zeroPartiallyOverriddenInFirst) {
+        this.zeroPartiallyOverriddenInFirst = zeroPartiallyOverriddenInFirst;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/SecondLevel.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/SecondLevel.java
new file mode 100644
index 0000000..66b5c27
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/SecondLevel.java
@@ -0,0 +1,42 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class SecondLevel extends FirstLevel {
+
+    private String inSecondLevel;
+
+    //hides inFirstLevel in FirstLevelGeneric
+    private String inFirstLevel;
+
+    public String getInSecondLevel() {
+        return inSecondLevel;
+    }
+
+    public void setInSecondLevel(String inSecondLevel) {
+        this.inSecondLevel = inSecondLevel;
+    }
+
+    @Override
+    public String getInFirstLevel() {
+        return inFirstLevel;
+    }
+
+    @Override
+    public void setInFirstLevel(String inFirstLevel) {
+        this.inFirstLevel = inFirstLevel;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/AbstractZeroLevelGeneric.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/AbstractZeroLevelGeneric.java
new file mode 100644
index 0000000..f7a074d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/AbstractZeroLevelGeneric.java
@@ -0,0 +1,35 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model.generics;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public abstract class AbstractZeroLevelGeneric<ZF, Z> {
+
+    private Z inZero;
+
+    protected ZF inZeroOverriddenInFirst;
+
+    abstract ZF getInZeroOverriddenInFirst();
+
+    abstract void setInZeroOverriddenInFirst(ZF value);
+
+    public Z getInZero() {
+        return inZero;
+    }
+
+    public void setInZero(Z inZero) {
+        this.inZero = inZero;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/AnotherGenericInterface.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/AnotherGenericInterface.java
new file mode 100644
index 0000000..c479f4f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/AnotherGenericInterface.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model.generics;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public interface AnotherGenericInterface<T> {
+
+    T getAnotherGenericValue();
+
+    void setAnotherGenericValue(T value);
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ExtendsExtendsPropagatedGenericClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ExtendsExtendsPropagatedGenericClass.java
new file mode 100644
index 0000000..c88d383
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ExtendsExtendsPropagatedGenericClass.java
@@ -0,0 +1,21 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model.generics;
+
+import java.math.BigDecimal;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ExtendsExtendsPropagatedGenericClass extends ExtendsPropagatedGenericClass<String, BigDecimal> {
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ExtendsPropagatedGenericClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ExtendsPropagatedGenericClass.java
new file mode 100644
index 0000000..cf2a7fd
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ExtendsPropagatedGenericClass.java
@@ -0,0 +1,21 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model.generics;
+
+import org.eclipse.yasson.defaultmapping.generics.model.PropagatedGenericClass;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ExtendsPropagatedGenericClass<A,B> extends PropagatedGenericClass<A,B> {
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/FirstLevelGeneric.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/FirstLevelGeneric.java
new file mode 100644
index 0000000..441f4d4
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/FirstLevelGeneric.java
@@ -0,0 +1,39 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model.generics;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class FirstLevelGeneric<F, Z> extends AbstractZeroLevelGeneric<Z, String> {
+
+    private F inFirstLevel;
+
+    @Override
+    public Z getInZeroOverriddenInFirst() {
+        return inZeroOverriddenInFirst;
+    }
+
+    @Override
+    public void setInZeroOverriddenInFirst(Z value) {
+        inZeroOverriddenInFirst = value;
+    }
+
+    public F getInFirstLevel() {
+        return inFirstLevel;
+    }
+
+    public void setInFirstLevel(F inFirstLevel) {
+        this.inFirstLevel = inFirstLevel;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/GenericInterface.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/GenericInterface.java
new file mode 100644
index 0000000..03feeb5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/GenericInterface.java
@@ -0,0 +1,24 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model.generics;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public interface GenericInterface<T> {
+
+    T getGenericValue();
+
+    void setGenericValue(T value);
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ImplementsGenericInterfaces.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ImplementsGenericInterfaces.java
new file mode 100644
index 0000000..32bdda5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/ImplementsGenericInterfaces.java
@@ -0,0 +1,43 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model.generics;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ImplementsGenericInterfaces<A, B> implements GenericInterface<A>, AnotherGenericInterface<B> {
+
+    private A a;
+
+    private B b;
+
+    @Override
+    public B getAnotherGenericValue() {
+        return b;
+    }
+
+    @Override
+    public void setAnotherGenericValue(B value) {
+        this.b = value;
+    }
+
+    @Override
+    public A getGenericValue() {
+        return a;
+    }
+
+    @Override
+    public void setGenericValue(A value) {
+        this.a = value;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/SecondLevelGeneric.java b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/SecondLevelGeneric.java
new file mode 100644
index 0000000..ec52fa0
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/inheritance/model/generics/SecondLevelGeneric.java
@@ -0,0 +1,30 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.inheritance.model.generics;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class SecondLevelGeneric<S,SF extends S,Z> extends FirstLevelGeneric<SF,Z> {
+
+    private S inSecondLevel;
+
+    public S getInSecondLevel() {
+        return inSecondLevel;
+    }
+
+    public void setInSecondLevel(S inSecondLevel) {
+        this.inSecondLevel = inSecondLevel;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpLong.java b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpLong.java
new file mode 100644
index 0000000..12b0e40
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpLong.java
@@ -0,0 +1,94 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.jsonp;
+
+import jakarta.json.JsonNumber;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Test class implementing {@link JsonNumber} interface.
+ *
+ * @author Dmitry Kornilov
+ */
+final public class JsonpLong implements JsonNumber {
+    private final long num;
+    private BigDecimal bigDecimal;  // assigning it lazily on demand
+
+    public JsonpLong(long num) {
+        this.num = num;
+    }
+
+    @Override
+    public int intValue() {
+        return bigDecimalValue().intValue();
+    }
+
+    @Override
+    public int intValueExact() {
+        return bigDecimalValue().intValueExact();
+    }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return bigDecimalValue().toBigInteger();
+    }
+
+    @Override
+    public BigInteger bigIntegerValueExact() {
+        return bigDecimalValue().toBigIntegerExact();
+    }
+
+
+    @Override
+    public boolean isIntegral() {
+        return true;
+    }
+
+    @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 ValueType getValueType() {
+        return ValueType.NUMBER;
+    }
+
+    @Override
+    public String toString() {
+        return Long.toString(num);
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpString.java b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpString.java
new file mode 100644
index 0000000..dbf959a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpString.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.jsonp;
+
+import jakarta.json.JsonString;
+
+/**
+ * Test class implementing {@link JsonString} interface.
+ *
+ * @author Dmitry Kornilov
+ */
+public class JsonpString implements JsonString {
+
+    private final String value;
+
+    public JsonpString(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 value.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof JsonString)) {
+            return false;
+        }
+        JsonString other = (JsonString) obj;
+        return getString().equals(other.getString());
+    }
+
+    @Override
+    public String toString() {
+        return "\"" + value + "\"";
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpTest.java
new file mode 100644
index 0000000..731c9f7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/JsonpTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.jsonp;
+
+import java.math.BigDecimal;
+
+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.JsonString;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.spi.JsonProvider;
+import org.eclipse.yasson.defaultmapping.jsonp.model.JsonpPojo;
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Default mapping JSONP integration tests.
+ *
+ * @author Dmitry Kornilov
+ */
+public class JsonpTest {
+
+    public static class JsonValueWrapper {
+        public JsonValue jsonValue;
+
+        public JsonValueWrapper(JsonValue jsonValue) {
+            this.jsonValue = jsonValue;
+        }
+
+        public JsonValueWrapper() {
+        }
+    }
+
+    @Test
+    public void testInnerJsonObject() {
+
+        final JsonBuilderFactory factory = Json.createBuilderFactory(null);
+        final JsonObject jsonObject = factory.createObjectBuilder()
+                .add("name", "home")
+                .add("city", "Prague")
+                .build();
+
+        final JsonObjectBuilder customerBuilder = factory.createObjectBuilder();
+        customerBuilder.add("f1", "abc123");
+        customerBuilder.add("f2", BigDecimal.TEN);
+        customerBuilder.add("f3", 12);
+        customerBuilder.add("city", jsonObject);
+
+        final JsonObjectBuilder wrapperBuilder = factory.createObjectBuilder();
+        wrapperBuilder.add("f1", "abc");
+        wrapperBuilder.add("cust", customerBuilder);
+
+        final JsonObject wrapper = wrapperBuilder.build();
+
+        String expected = "{\"f1\":\"abc\",\"cust\":{\"f1\":\"abc123\",\"f2\":10,\"f3\":12,\"city\":{\"name\":\"home\",\"city\":\"Prague\"}}}";
+        assertEquals(expected, defaultJsonb.toJson(wrapper));
+
+        JsonObject result = defaultJsonb.fromJson(expected, JsonObject.class);
+        assertEquals("home", result.getJsonObject("cust").getJsonObject("city").getString("name"));
+        assertEquals("abc123", result.getJsonObject("cust").getString("f1"));
+        assertEquals("abc123", result.getJsonObject("cust").getString("f1"));
+
+    }
+
+    @Test
+    public void testMarshallJsonArray() {
+
+        final JsonBuilderFactory factory = Json.createBuilderFactory(null);
+        final JsonArray jsonArray = factory.createArrayBuilder()
+                .add(1)
+                .add(2)
+                .build();
+
+        assertEquals("{\"jsonValue\":[1,2]}", defaultJsonb.toJson(new JsonValueWrapper(jsonArray)));
+    }
+
+    @Test
+    public void testMarshallJsonValue() {
+        assertEquals("{\"jsonValue\":true}", defaultJsonb.toJson(new JsonValueWrapper(JsonValue.TRUE)));
+    }
+
+    @Test
+    public void testMarshallJsonNumber() {
+                assertEquals("{\"jsonValue\":10}", defaultJsonb.toJson(new JsonValueWrapper(new JsonpLong(10))));
+    }
+
+    @Test
+    public void testMarshallJsonString() {
+                assertEquals("{\"jsonValue\":\"hello\"}", defaultJsonb.toJson(new JsonValueWrapper(new JsonpString("hello"))));
+    }
+
+    @Test
+    public void testJsonPojo() {
+        JsonbConfig config = new JsonbConfig();
+//        config.withFormatting(true);
+        Jsonb jsonb = JsonbBuilder.create(config);
+
+
+        JsonpPojo pojo = new JsonpPojo();
+        final JsonObjectBuilder obj1builder = JsonProvider.provider().createObjectBuilder();
+        obj1builder.add("strVal", "string value");
+        obj1builder.add("numVal", 2.0d);
+        obj1builder.addNull("nullVal");
+        obj1builder.add("boolVal", Boolean.TRUE);
+
+        final JsonObjectBuilder obj2Builder = JsonProvider.provider().createObjectBuilder();
+        obj2Builder.add("innerStr", "string val");
+        obj2Builder.add("innerNum", 11.1d);
+        final JsonObject obj2 = obj2Builder.build();
+
+        JsonArrayBuilder array1Builder = JsonProvider.provider().createArrayBuilder();
+        array1Builder.addNull().add(false).add(11L).add(BigDecimal.TEN).add("array STR value").add(obj2);
+        JsonArray jsonArray1 = array1Builder.build();
+
+        obj1builder.add("innerJsonObject", obj2);
+        obj1builder.add("innerArrayObject", jsonArray1);
+
+        final JsonObject obj1 = obj1builder.build();
+        pojo.jsonObject = obj1;
+
+        JsonArrayBuilder arrayBuilder = JsonProvider.provider().createArrayBuilder();
+        arrayBuilder.add(obj1).add(true).add(obj2).add(101.0d).add(BigDecimal.TEN);
+        pojo.jsonArray = arrayBuilder.build();
+
+
+        String expected = "{\"jsonArray\":[{\"strVal\":\"string value\",\"numVal\":2.0,\"nullVal\":null,\"boolVal\":true,\"innerJsonObject\":{\"innerStr\":\"string val\",\"innerNum\":11.1},\"innerArrayObject\":[null,false,11,10,\"array STR value\",{\"innerStr\":\"string val\",\"innerNum\":11.1}]},true,{\"innerStr\":\"string val\",\"innerNum\":11.1},101.0,10],\"jsonObject\":{\"strVal\":\"string value\",\"numVal\":2.0,\"nullVal\":null,\"boolVal\":true,\"innerJsonObject\":{\"innerStr\":\"string val\",\"innerNum\":11.1},\"innerArrayObject\":[null,false,11,10,\"array STR value\",{\"innerStr\":\"string val\",\"innerNum\":11.1}]}}";
+        final String actual = jsonb.toJson(pojo);
+        assertEquals(expected, actual);
+
+        JsonpPojo result = jsonb.fromJson(expected, JsonpPojo.class);
+        assertEquals(pojo.jsonObject, result.jsonObject);
+        assertEquals(pojo.jsonArray, result.jsonArray);
+    }
+
+    @Test
+    public void testJsonObject() {
+        final JsonObjectBuilder objBuilder = JsonProvider.provider().createObjectBuilder();
+        objBuilder.add("boolTrue", Boolean.TRUE).add("boolFalse", Boolean.FALSE)
+                .addNull("null").add("str", "String");
+
+        JsonArrayBuilder arrBuilder = JsonProvider.provider().createArrayBuilder();
+        arrBuilder.add(11L).add(Boolean.FALSE).add(BigDecimal.TEN);
+        objBuilder.add("array", arrBuilder);
+
+        JsonObject object = objBuilder.build();
+
+        String expected = "{\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"str\":\"String\",\"array\":[11,false,10]}";
+        assertEquals(expected, defaultJsonb.toJson(object));
+
+        JsonObject result = defaultJsonb.fromJson(expected, JsonObject.class);
+
+        assertEquals(object, result);
+    }
+
+    @Test
+    public void testJsonArray() {
+
+        JsonArrayBuilder arrBuilder = JsonProvider.provider().createArrayBuilder();
+        arrBuilder.add(11L).add(Boolean.FALSE).add(BigDecimal.TEN);
+
+        JsonObjectBuilder objBuilder = JsonProvider.provider().createObjectBuilder();
+        objBuilder.add("boolTrue", Boolean.TRUE).add("boolFalse", Boolean.FALSE)
+                .addNull("null").add("str", "String");
+
+        arrBuilder.add(objBuilder);
+
+        JsonArray arr = arrBuilder.build();
+
+        String expected = "[11,false,10,{\"boolTrue\":true,\"boolFalse\":false,\"null\":null,\"str\":\"String\"}]";
+        assertEquals(expected, defaultJsonb.toJson(arr));
+
+        JsonArray result = defaultJsonb.fromJson(expected, JsonArray.class);
+
+        assertEquals(arr, result);
+    }
+
+    @Test
+    public void testJsonObjectAsValue() {
+        final JsonValueWrapper jsonValueWrapper = defaultJsonb.fromJson("{ \"jsonValue\" : { \"stringInstance\" : \"Test String\" } }", JsonValueWrapper.class);
+        assertEquals("Test String", ((JsonObject) jsonValueWrapper.jsonValue).getString("stringInstance"));
+    }
+
+    @Test
+    public void testJsonValueString() {
+        JsonValueWrapper pojo = new JsonValueWrapper(Json.createValue("abc"));
+        String json = defaultJsonb.toJson(pojo);
+        assertEquals("{\"jsonValue\":\"abc\"}", json);
+
+        JsonValueWrapper result = defaultJsonb.fromJson("{\"jsonValue\":\"def\"}", JsonValueWrapper.class);
+        assertTrue(result.jsonValue instanceof  JsonString);
+        assertEquals("def", ((JsonString)result.jsonValue).getString());
+    }
+
+    @Test
+    public void testJsonValueAsObject() {
+        JsonObject build = Json.createObjectBuilder().add("prop1", "val1")
+                .add("prop2", "val2")
+                .add("innerObj1", Json.createObjectBuilder().add("inner1", "innerVal1").build())
+                .build();
+        JsonValueWrapper pojo = new JsonValueWrapper(build);
+        String expected = "{\"jsonValue\":{\"prop1\":\"val1\",\"prop2\":\"val2\",\"innerObj1\":{\"inner1\":\"innerVal1\"}}}";
+        String json = defaultJsonb.toJson(pojo);
+        assertEquals(expected, json);
+
+        JsonValueWrapper result = defaultJsonb.fromJson(expected, JsonValueWrapper.class);
+        assertTrue(result.jsonValue instanceof JsonObject);
+        JsonObject jsonObject = (JsonObject) result.jsonValue;
+        assertEquals("val1", jsonObject.getString("prop1"));
+        assertEquals("innerVal1", jsonObject.getJsonObject("innerObj1").getString("inner1"));
+    }
+
+    @Test
+    public void testJsonValueAsArray() {
+        JsonArray jsonArray = Json.createArrayBuilder().add(1).add(2).add(3).add(Json.createObjectBuilder().add("a","b").build()).build();
+        JsonValueWrapper pojo = new JsonValueWrapper(jsonArray);
+        String expected = "{\"jsonValue\":[1,2,3,{\"a\":\"b\"}]}";
+        String json = defaultJsonb.toJson(pojo);
+        assertEquals(expected, json);
+
+        JsonValueWrapper result = defaultJsonb.fromJson(expected, JsonValueWrapper.class);
+        assertTrue(result.jsonValue instanceof JsonArray);
+        JsonArray resultArray = (JsonArray) result.jsonValue;
+        assertEquals(1, resultArray.getInt(0));
+        assertEquals(2, resultArray.getInt(1));
+        assertEquals(3, resultArray.getInt(2));
+        assertEquals("b", resultArray.getJsonObject(3).getString("a"));
+
+    }
+
+    @Test
+    public void testJsonNullValue() {
+        JsonValueWrapper pojo = new JsonValueWrapper(null);
+        String expected = "{}";
+        String json = defaultJsonb.toJson(pojo);
+        assertThat(json, is(expected));
+        JsonValueWrapper deserialized = defaultJsonb.fromJson(expected, JsonValueWrapper.class);
+        assertThat(deserialized.jsonValue, nullValue());
+        deserialized = defaultJsonb.fromJson("{\"jsonValue\":null}", JsonValueWrapper.class);
+        assertThat(deserialized.jsonValue, is(JsonValue.NULL));
+    }
+
+    @Test
+    public void testJsonpNullValues() {
+        JsonpPojo pojo = new JsonpPojo();
+        String expected = "{}";
+        String nullValues = "{\"jsonObject\":null, \"jsonArray\":null, \"jsonNumber\":null, \"jsonString\":null, "
+                + "\"jsonValue\":null }";
+        String json = defaultJsonb.toJson(pojo);
+        assertThat(json, is(expected));
+        JsonpPojo deserialized = defaultJsonb.fromJson(expected, JsonpPojo.class);
+        assertThat(deserialized.jsonObject, nullValue());
+        deserialized = defaultJsonb.fromJson(nullValues, JsonpPojo.class);
+        assertThat(deserialized.jsonObject, nullValue());
+        assertThat(deserialized.jsonArray, nullValue());
+        assertThat(deserialized.jsonNumber, nullValue());
+        assertThat(deserialized.jsonString, nullValue());
+        assertThat(deserialized.jsonValue, is(JsonValue.NULL));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/model/JsonpPojo.java b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/model/JsonpPojo.java
new file mode 100644
index 0000000..8f34952
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/jsonp/model/JsonpPojo.java
@@ -0,0 +1,31 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.jsonp.model;
+
+import jakarta.json.*;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonpPojo {
+
+    public JsonObject jsonObject;
+
+    public JsonArray jsonArray;
+
+    public JsonNumber jsonNumber;
+
+    public JsonString jsonString;
+
+    public JsonValue jsonValue;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Addressable.java b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Addressable.java
new file mode 100644
index 0000000..95a45a7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Addressable.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.lambda;
+
+/**
+ * A functional interface with no default properties.
+ */
+public interface Addressable {
+
+    /**
+     * Public readable property.
+     *
+     * @return name to be addressed with
+     */
+    String getName();
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Cat.java b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Cat.java
new file mode 100644
index 0000000..451ee31
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Cat.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.lambda;
+
+/**
+ * Class used to control serialization of lambda expression generated from functional interfaces with defaults.
+ */
+public class Cat implements Pet {
+
+    private String name;
+
+    Cat(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/lambda/LambdaExpressionTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/LambdaExpressionTest.java
new file mode 100644
index 0000000..09dde87
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/LambdaExpressionTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.lambda;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+/**
+ * Test marshalling objects generated using lambda expressions.
+ *
+ * Scenarios test lambda-generated objects serialization against concrete class objects serialization. It is thus
+ * assumed that concrete class behaviour is tested successfully elsewhere.
+ */
+public class LambdaExpressionTest {
+
+    @Test
+    public void testMarshallFunctionalInterface() {
+        String name = "WALL-E";
+        Addressable control = new Robot(name);
+        Addressable lambda = () -> name;
+        assertEquals(defaultJsonb.toJson(control), defaultJsonb.toJson(lambda));
+    }
+
+    @Test
+    public void testMarshallFunctionalInterfaceWithDefaultProperties() {
+        String name = "Cheshire";
+        Pet control = new Cat(name);
+        Pet lambda = () -> name;
+        assertEquals(defaultJsonb.toJson(control), defaultJsonb.toJson(lambda));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Pet.java b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Pet.java
new file mode 100644
index 0000000..6994e2b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Pet.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.lambda;
+
+/**
+ * A functional interface with one default property.
+ */
+public interface Pet extends Addressable {
+
+    /**
+     * Public readable property with default value.
+     *
+     * @return pet's age in months
+     */
+    default int getAge() {
+        return 0;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Robot.java b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Robot.java
new file mode 100644
index 0000000..900363c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/lambda/Robot.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.lambda;
+
+/**
+ * Class used to control serialization of lambda expression generated from functional interfaces with no defaults.
+ */
+public class Robot implements Addressable {
+
+    private String name;
+
+    Robot(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getName() {
+        return this.name;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/ClassModifiersTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/ClassModifiersTest.java
new file mode 100644
index 0000000..6ecd6c3
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/ClassModifiersTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.Assertions;
+import org.eclipse.yasson.defaultmapping.modifiers.model.ChildOfPackagePrivateParent;
+
+/**
+ * Test access modifiers on classes
+ *
+ * @author David Kral
+ */
+public class ClassModifiersTest {
+
+    @Test
+    public void testPackagePrivateParent() {
+        ChildOfPackagePrivateParent child = new ChildOfPackagePrivateParent();
+        child.id = 1;
+        child.name = "SomeName";
+        String json = defaultJsonb.toJson(child);
+        assertEquals("{\"id\":1,\"name\":\"SomeName\"}", json);
+        ChildOfPackagePrivateParent result = defaultJsonb.fromJson(json, ChildOfPackagePrivateParent.class);
+        assertEquals(child.id, result.id);
+        assertEquals(child.name, result.name);
+    }
+
+
+    class NestedPackageParent {
+        public int id;
+    }
+
+    public class NestedPackageChild extends NestedPackageParent {
+        public String name;
+    }
+
+    @Test
+    public void testNestedPackagePrivateParent() {
+        NestedPackageChild child = new NestedPackageChild();
+        child.id = 1;
+        child.name = "SomeName";
+        Assertions.shouldFail(() -> defaultJsonb.toJson(child),
+                msg -> msg.contains("java.lang.IllegalAccessException") &&
+                       msg.contains("Error accessing field 'id' declared in " +
+                                    "'class org.eclipse.yasson.defaultmapping.modifiers.ClassModifiersTest$NestedPackageParent'"));
+    }
+
+    private class NestedPrivateParent {
+        public int id;
+    }
+
+    public class NestedPrivateChild extends NestedPrivateParent {
+        public String name;
+    }
+
+    @Test
+    public void testNestedPrivateParent() {
+        NestedPrivateChild child = new NestedPrivateChild();
+        child.id = 1;
+        child.name = "SomeName";
+        Assertions.shouldFail(() -> defaultJsonb.toJson(child),
+                msg -> msg.contains("java.lang.IllegalAccessException") &&
+                       msg.contains("Error accessing field 'id' declared in " +
+                                    "'class org.eclipse.yasson.defaultmapping.modifiers.ClassModifiersTest$NestedPrivateParent'"));
+    }
+
+
+    static class NestedStaticPackageParent {
+        public int id;
+    }
+
+    public static class NestedStaticPackageChild extends NestedStaticPackageParent {
+        public String name;
+    }
+
+    @Test
+    public void testNestedStaticPackagePrivateParent() {
+        NestedStaticPackageChild child = new NestedStaticPackageChild();
+        child.id = 1;
+        child.name = "SomeName";
+        Assertions.shouldFail(() -> defaultJsonb.toJson(child),
+                msg -> msg.contains("java.lang.IllegalAccessException") &&
+                       msg.contains("Error accessing field 'id' declared in " +
+                                    "'class org.eclipse.yasson.defaultmapping.modifiers.ClassModifiersTest$NestedStaticPackageParent'"));
+    }
+
+    private static class NestedStaticPrivateParent {
+        public int id;
+    }
+
+    public static class NestedStaticPrivateChild extends NestedStaticPrivateParent {
+        public String name;
+    }
+
+    @Test
+    public void testNestedStaticPrivateParent() {
+        NestedStaticPrivateChild child = new NestedStaticPrivateChild();
+        child.id = 1;
+        child.name = "SomeName";
+        Assertions.shouldFail(() -> defaultJsonb.toJson(child),
+                msg -> msg.contains("java.lang.IllegalAccessException") &&
+                       msg.contains("Error accessing field 'id' declared in " +
+                                   "'class org.eclipse.yasson.defaultmapping.modifiers.ClassModifiersTest$NestedStaticPrivateParent'"));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/DefaultMappingModifiersTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/DefaultMappingModifiersTest.java
new file mode 100644
index 0000000..37e8239
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/DefaultMappingModifiersTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.defaultmapping.modifiers.model.Person;
+import org.eclipse.yasson.defaultmapping.modifiers.model.FieldModifiersClass;
+import org.eclipse.yasson.defaultmapping.modifiers.model.MethodModifiersClass;
+import org.eclipse.yasson.defaultmapping.modifiers.model.PrivateConstructorClass;
+import org.eclipse.yasson.defaultmapping.modifiers.model.ProtectedConstructorClass;
+
+import jakarta.json.bind.JsonbException;
+
+/**
+ * Test access modifiers for default mapping.
+ *
+ * @author Roman Grigoriadi
+ */
+public class DefaultMappingModifiersTest {
+
+    @Test
+    public void testFieldModifiers() {
+        FieldModifiersClass fieldModifiersClass = new FieldModifiersClass();
+        assertEquals("{\"finalString\":\"FINAL_STRING\"}", defaultJsonb.toJson(fieldModifiersClass));
+        FieldModifiersClass result = defaultJsonb.fromJson("{\"finalString\":\"FINAL_STRING\",\"staticString\":\"STATIC_STRING\",\"transientString\":\"TRANSIENT_STRING\"}", FieldModifiersClass.class);
+        //no setter throwing illegal has been called.
+    }
+
+    @Test
+    public void testMethodModifiers() {
+        MethodModifiersClass methodModifiers = new MethodModifiersClass();
+        methodModifiers.publicFieldWithoutMethods = "WITHOUT_METHODS";
+
+        String validJson = "{\"getterWithoutFieldValue\":\"GETTER_WITHOUT_FIELD\",\"publicFieldWithoutMethods\":\"WITHOUT_METHODS\"}";
+        assertEquals(validJson, defaultJsonb.toJson(methodModifiers));
+
+        MethodModifiersClass result = defaultJsonb.fromJson("{\"publicFieldWithPrivateMethods\":\"value\"}", MethodModifiersClass.class);
+        assertNull(result.publicFieldWithPrivateMethods);
+
+        result = defaultJsonb.fromJson(validJson, MethodModifiersClass.class);
+        assertEquals("WITHOUT_METHODS", result.publicFieldWithoutMethods);
+    }
+
+    @Test
+    public void testConstructorModifiers() {
+        try{
+            ProtectedConstructorClass instance = defaultJsonb.fromJson("{\"randomField\":\"test\"}", ProtectedConstructorClass.class);
+            assertEquals(instance.randomField, "test");
+        } catch (JsonbException e){
+            fail("No exception should be thrown for protected constructor");
+            throw e;
+        }
+        try {
+        	defaultJsonb.fromJson("{\"randomField\":\"test\"}", PrivateConstructorClass.class);
+            fail("Exception should have been thrown");
+        }catch (JsonbException e){
+            assertTrue(e.getMessage().endsWith("Can't create instance"));
+        }
+    }
+
+    @Test
+    public void testMultipleInstancesOfSameType() {
+        Person person = new Person();
+        Person personTwo = new Person();
+        person.name = "Person 1";
+        personTwo.name = "Person 2";
+        person.child = personTwo;
+
+        assertEquals("{\"child\":{\"name\":\"Person 2\"},\"name\":\"Person 1\"}", bindingJsonb.toJson(person));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/ChildOfPackagePrivateParent.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/ChildOfPackagePrivateParent.java
new file mode 100644
index 0000000..666505a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/ChildOfPackagePrivateParent.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers.model;
+
+/**
+ * @author David Kral
+ */
+public class ChildOfPackagePrivateParent extends PackagePrivateParent {
+
+    public String name;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/FieldModifiersClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/FieldModifiersClass.java
new file mode 100644
index 0000000..d5b4952
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/FieldModifiersClass.java
@@ -0,0 +1,55 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class FieldModifiersClass {
+
+    private final String finalString = "FINAL_STRING";
+
+    private static final Long serialVersionUID = Long.MAX_VALUE;
+
+    private static String staticString = "STATIC_STRING";
+
+    private transient String transientString = "TRANSIENT_STRING";
+
+    public String getFinalString() {
+        return finalString;
+    }
+
+    public void setFinalString() {
+        throw new IllegalStateException();
+    }
+
+    public static Long getSerialVersionUID() {
+        throw new IllegalStateException();
+    }
+
+    public String getTransientString() {
+        throw new IllegalStateException();
+    }
+
+    public void setTransientString(String transientString) {
+        throw new IllegalStateException();
+    }
+
+    public static String getStaticString() {
+        throw new IllegalStateException();
+    }
+
+    public static void setStaticString(String staticString) {
+        throw new IllegalStateException();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/MethodModifiersClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/MethodModifiersClass.java
new file mode 100644
index 0000000..7fee06d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/MethodModifiersClass.java
@@ -0,0 +1,47 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers.model;
+
+import java.util.function.Consumer;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class MethodModifiersClass {
+
+    private Consumer<String> setterWithoutFieldConsumer = s -> {};
+
+    public String publicFieldWithPrivateMethods;
+
+    public String publicFieldWithoutMethods;
+
+    public void setSetterWithoutFieldConsumer(Consumer<String> setterWithoutFieldConsumer) {
+        this.setterWithoutFieldConsumer = setterWithoutFieldConsumer;
+    }
+
+    public String getGetterWithoutFieldValue() {
+        return "GETTER_WITHOUT_FIELD";
+    }
+
+    public void setGetterWithoutFieldValue(String value) {
+        setterWithoutFieldConsumer.accept(value);
+    }
+
+    private String getPublicFieldWithPrivateMethods() {
+        return publicFieldWithPrivateMethods;
+    }
+
+    private void setPublicFieldWithPrivateMethods(String publicFieldWithPrivateMethods) {
+        this.publicFieldWithPrivateMethods = publicFieldWithPrivateMethods;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/PackagePrivateParent.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/PackagePrivateParent.java
new file mode 100644
index 0000000..acd4484
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/PackagePrivateParent.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers.model;
+
+/**
+ * @author David Kral
+ */
+class PackagePrivateParent {
+
+    public int id;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/Person.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/Person.java
new file mode 100644
index 0000000..f873ae7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/Person.java
@@ -0,0 +1,24 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers.model;
+
+/**
+ * @author David Kral
+ */
+public class Person {
+
+    public String name;
+
+    public Person child;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/PrivateConstructorClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/PrivateConstructorClass.java
new file mode 100644
index 0000000..3f8b411
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/PrivateConstructorClass.java
@@ -0,0 +1,24 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers.model;
+
+/**
+ * @author David Kral
+ */
+public class PrivateConstructorClass {
+
+    public String randomField;
+
+    private PrivateConstructorClass(){
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/ProtectedConstructorClass.java b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/ProtectedConstructorClass.java
new file mode 100644
index 0000000..505f9a1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/modifiers/model/ProtectedConstructorClass.java
@@ -0,0 +1,24 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.modifiers.model;
+
+/**
+ * @author David Kral
+ */
+public class ProtectedConstructorClass {
+
+    public String randomField;
+
+    protected ProtectedConstructorClass(){
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/properties/PropertiesTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/properties/PropertiesTest.java
new file mode 100644
index 0000000..6254e97
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/properties/PropertiesTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.properties;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+
+import java.io.IOException;
+import java.util.Locale;
+
+/**
+ * This class contains properties tests
+ *
+ * @author David Kral
+ */
+public class PropertiesTest {
+
+    @Test
+    public void testPropertiesWithoutLocale() throws IOException {
+        String template = "Process class: {0} from json using converter: {1}";
+        String message = Messages.getMessage(MessageKeys.PROCESS_FROM_JSON);
+
+        assertEquals(template, message);
+    }
+
+    @Test
+    public void testPropertiesWithLocale() throws IOException {
+        String templateCS = "Zpracovávám třídu: {0} do jsonu za použití convertoru: {1}";
+        String messageCS = Messages.getMessage(MessageKeys.PROCESS_TO_JSON, new Locale("cs"));
+        String templateEN = "Process class: {0} to json using converter: {1}";
+        String messageEN = Messages.getMessage(MessageKeys.PROCESS_TO_JSON, new Locale("en"));
+
+        assertEquals(templateCS, messageCS);
+        assertEquals(templateEN, messageEN);
+    }
+
+    @Test
+    public void testPropertiesAttributeSetting() throws IOException {
+        String template = "Zpracovávám třídu: Test do jsonu za použití convertoru: Test1";
+        String message = Messages.getMessage(MessageKeys.PROCESS_TO_JSON, new Locale("cs"), "Test", "Test1");
+
+        assertEquals(template, message);
+    }
+
+}
+
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/CustomerTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/CustomerTest.java
new file mode 100644
index 0000000..8e8a762
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/CustomerTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.defaultmapping.specific.model.Address;
+import org.eclipse.yasson.defaultmapping.specific.model.Customer;
+import org.eclipse.yasson.defaultmapping.specific.model.Street;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public abstract class CustomerTest {
+
+    protected void assertCustomerValues(Customer customer, String customerName) {
+        assertEquals(customerName, customer.getName());
+        assertEquals(Integer.valueOf(33), customer.getAge());
+        /*assertEquals(1, customer.getAddresses().size());
+        for (Address address : customer.getAddresses()) {
+            assertEquals("Prague", address.getTown());
+            assertEquals("Zoubkova88", address.getStreet().getName());
+            assertEquals(Integer.valueOf(111), address.getStreet().getNumber());
+        }*/
+
+        if (customer.getStrings() != null) {
+            assertEquals(2, customer.getStrings().size());
+            assertEquals("green", customer.getStrings().get(0));
+            assertEquals("yellow", customer.getStrings().get(1));
+        }
+
+        if (customer.getIntegers() != null) {
+            assertEquals(2, customer.getIntegers().size());
+            assertEquals(Integer.valueOf(0),  customer.getIntegers().get(0));
+            assertEquals(Integer.valueOf(1), customer.getIntegers().get(1));
+        }
+
+        /*assertEquals(2, customer.getStringIntegerMap().size());
+        assertEquals(Integer.valueOf(1), customer.getStringIntegerMap().get("first"));
+        assertEquals(Integer.valueOf(2), customer.getStringIntegerMap().get("second"));*/
+    }
+
+    protected static Customer createCustomer(String customerName) {
+        Street street = new Street("Zoubkova", 111);
+        Address address = new Address(street, "Prague");
+        Customer customer = new Customer(33, customerName);
+
+        List<Address> addresses = new ArrayList<>();
+        addresses.add(address);
+        customer.setAddresses(addresses);
+
+        List<String> strings = new ArrayList<>();
+        strings.add("green");
+        strings.add("yellow");
+        customer.setStrings(strings);
+
+        List<Integer> integers = new ArrayList<>();
+        integers.add(0);
+        integers.add(1);
+        customer.setIntegers(integers);
+
+        Map<String, Integer> stringIntegerMap = new HashMap<>();
+        stringIntegerMap.put("first", 1);
+        stringIntegerMap.put("second", 2);
+        customer.setStringIntegerMap(stringIntegerMap);
+
+        List<List<Integer>> listOfListsOfIntegers = new ArrayList<>();
+        for(int i=0; i<3; i++) {
+            List<Integer> integerList = new ArrayList<>();
+            integerList.add(0);
+            integerList.add(1);
+            integerList.add(2);
+            listOfListsOfIntegers.add(integerList);
+        }
+
+        customer.setListOfListsOfIntegers(listOfListsOfIntegers);
+
+        return customer;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/JsonStreamsTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/JsonStreamsTest.java
new file mode 100644
index 0000000..6de9ac1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/JsonStreamsTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests calling JSONB with {@link java.util.stream.Stream} and {@link Readable}
+ *
+ * @author Roman Grigoriadi
+ */
+public class JsonStreamsTest {
+    private static final String CHARSET = "UTF8";
+
+    @Test
+    public void testUnmarshall() throws Exception {
+
+        String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
+
+        Map<String, String> result = defaultJsonb.fromJson(new InputStreamReader(new ByteArrayInputStream(json.getBytes(CHARSET)), Charset.forName(CHARSET)), new TestTypeToken<HashMap<String, String>>(){}.getType());
+        assertMapValues(result);
+
+        result = defaultJsonb.fromJson(new ByteArrayInputStream(json.getBytes(CHARSET)), new TestTypeToken<HashMap<String, String>>() {}.getType());
+        assertMapValues(result);
+    }
+
+    @Test
+    public void testMarshall() throws Exception {
+        String expected = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
+        int len = expected.getBytes(CHARSET).length;
+
+        Map<String, String> strMap = new HashMap<>();
+        strMap.put("key1", "value1");
+        strMap.put("key2", "value2");
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(len);
+        defaultJsonb.toJson(strMap, baos);
+        assertEquals(expected, baos.toString(CHARSET));
+
+        baos = new ByteArrayOutputStream(len);
+        OutputStreamWriter writer = new OutputStreamWriter(baos, Charset.forName(CHARSET));
+        defaultJsonb.toJson(strMap, writer);
+        writer.close();
+
+        assertEquals(expected, baos.toString(CHARSET));
+    }
+
+    private static void assertMapValues(Map<String, String> result) {
+        assertEquals(2, result.size());
+        assertEquals("value1", result.get("key1"));
+        assertEquals("value2", result.get("key2"));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/NullTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/NullTest.java
new file mode 100644
index 0000000..34be5dc
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/NullTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020 Payara Foundation 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific;
+
+import org.eclipse.yasson.defaultmapping.specific.model.StreetWithPrimitives;
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.specific.model.Street;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class NullTest {
+
+    @Test
+    public void testSetsNullIntoFields() {
+        String json = "{\"name\":null,\"number\":null}";
+
+        Street result = defaultJsonb.fromJson(json, Street.class);
+        //these have default initialization value
+        assertNull(result.getName());
+        assertNull(result.getNumber());
+    }
+//
+//    @Test
+//    public void testSetsNullToPrimitives() {
+//        String json = "{\"name\":null,\"number\":null}";
+//
+//        StreetWithPrimitives result = defaultJsonb.fromJson(json, StreetWithPrimitives.class);
+//        //these have default initialization value
+//        assertNull(result.getName());
+//        assertNull(result.getNumber());
+//    }
+
+    @Test
+    public void testDeserializeNull() {
+        assertNull(defaultJsonb.fromJson("null", Object.class));
+    }
+
+    @Test
+    public void testDeserializeNullPojo() {
+        assertNull(defaultJsonb.fromJson("null", Street.class));
+    }
+
+    @Test
+    public void testDeserializeNullList() {
+        assertNull(defaultJsonb.fromJson("null", new TestTypeToken<List<Integer>>() {}.getType()));
+    }
+
+    @Test
+    public void testDeserializeNullMap() {
+        assertNull(defaultJsonb.fromJson("null", new TestTypeToken<Map<String, Street>>() {}.getType()));
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/ObjectGraphTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/ObjectGraphTest.java
new file mode 100644
index 0000000..632207e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/ObjectGraphTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.defaultmapping.specific.model.Customer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ObjectGraphTest extends CustomerTest {
+    private static final String EXPECTED = "{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"friends\":{\"firstFriend\":{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"integers\":[0,1],\"listOfListsOfIntegers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"Jasons first friend\",\"stringIntegerMap\":{\"first\":1,\"second\":2},\"strings\":[\"green\",\"yellow\"]},\"secondFriend\":{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"integers\":[0,1],\"listOfListsOfIntegers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"Jasons second friend\",\"stringIntegerMap\":{\"first\":1,\"second\":2},\"strings\":[\"green\",\"yellow\"]}},\"integers\":[0,1],\"listOfListsOfIntegers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"Root Jason Customer\",\"stringIntegerMap\":{\"first\":1,\"second\":2},\"strings\":[\"green\",\"yellow\"]}";
+
+    @Test
+    public void testObjectToJson() {
+        Customer customer = createCustomer("Root Jason Customer");
+
+        Map<String, Customer> friends = new HashMap<>();
+        friends.put("firstFriend", createCustomer("Jasons first friend"));
+        friends.put("secondFriend", createCustomer("Jasons second friend"));
+        customer.setFriends(friends);
+
+        assertEquals(EXPECTED, bindingJsonb.toJson(customer));
+    }
+
+    @Test
+    public void testObjectFromJson() {
+        Customer customer = bindingJsonb.fromJson(EXPECTED, Customer.class);
+        assertCustomerValues(customer, "Root Jason Customer");
+        assertEquals(2, customer.getFriends().size());
+        assertCustomerValues(customer.getFriends().get("firstFriend"), "Jasons first friend");
+        assertCustomerValues(customer.getFriends().get("secondFriend"), "Jasons second friend");
+    }
+
+    @Test
+    public void testSimpleObject() {
+        Person person = new Person();
+        person.name = "David";
+        person.surname = "Kral";
+        String json = bindingJsonb.toJson(person);
+        Person deser = bindingJsonb.fromJson(json, Person.class);
+        System.out.println();
+    }
+
+    public static class Person {
+
+        public String name;
+
+        public String surname;
+
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/OptionalTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/OptionalTest.java
new file mode 100644
index 0000000..8bb2444
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/OptionalTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2015, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2020 Payara Foundation 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific;
+
+import org.eclipse.yasson.defaultmapping.specific.model.SpecificOptionalWrapper;
+import org.junit.jupiter.api.*;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+import org.eclipse.yasson.defaultmapping.specific.model.OptionalWrapper;
+import org.eclipse.yasson.defaultmapping.specific.model.NotMatchingGettersAndSetters;
+import org.eclipse.yasson.defaultmapping.specific.model.Street;
+import org.eclipse.yasson.internal.JsonBindingBuilder;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import java.util.*;
+
+/**
+ * Default mapping Optional* tests.
+ *
+ * @author Dmitry Kornilov
+ */
+public class OptionalTest {
+
+
+    @Test
+    public void testOptionalString() {
+        assertEquals("{\"value\":\"abc\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(Optional.of("abc"))));
+
+        ScalarValueWrapper<Optional> result = bindingJsonb.fromJson("{\"value\":\"abc\"}", new TestTypeToken<ScalarValueWrapper<Optional>>() {}.getType());
+        assertEquals(Optional.of("abc"), result.getValue());
+    }
+
+    @Test
+    public void testOptionalObject() {
+        final OptionalWrapper optionalWrapper = new OptionalWrapper();
+        Street street = new Street("Xaveriova", 110);
+        optionalWrapper.setStreetOptional(Optional.of(street));
+
+        assertEquals("{\"streetOptional\":{\"name\":\"Xaveriova\",\"number\":110}}", bindingJsonb.toJson(optionalWrapper));
+
+        OptionalWrapper result = bindingJsonb.fromJson("{\"streetOptional\":{\"name\":\"Xaveriova\",\"number\":110}}", OptionalWrapper.class);
+        assertTrue(result.getStreetOptional().isPresent());
+        assertEquals("Xaveriova", result.getStreetOptional().get().getName());
+        assertEquals(Integer.valueOf(110), result.getStreetOptional().get().getNumber());
+    }
+
+    @Test
+    public void testMarshallOptional() {
+        assertEquals("{}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalInt.empty())));
+        assertEquals("{}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalLong.empty())));
+        assertEquals("{}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalDouble.empty())));
+        assertEquals("{\"value\":10}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalInt.of(10))));
+        assertEquals("{\"value\":100}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalLong.of(100L))));
+        assertEquals("{\"value\":10.0}", bindingJsonb.toJson(new ScalarValueWrapper<>(OptionalDouble.of(10.0D))));
+
+        final ScalarValueWrapper<OptionalInt> result = bindingJsonb.fromJson("{\"value\":10}", new TestTypeToken<ScalarValueWrapper<OptionalInt>>() {}.getType());
+        assertEquals(OptionalInt.of(10), result.getValue());
+    }
+
+    @Test
+    public void testMarshallOptionalObject() {
+        assertEquals("{}", bindingJsonb.toJson(new ScalarValueWrapper<>(Optional.empty())));
+        assertEquals("{\"id\":1,\"name\":\"Cust1\"}", bindingJsonb.toJson(Optional.of(new Customer(1, "Cust1"))));
+
+    }
+
+    @Test
+    public void testMarshallOptionalIntArray() {
+        final OptionalInt[] array = {OptionalInt.of(1), OptionalInt.of(2), OptionalInt.empty()};
+        assertEquals("[1,2,null]", bindingJsonb.toJson(array));
+
+        OptionalInt[] result = bindingJsonb.fromJson("[1,2,null]", new TestTypeToken<OptionalInt[]>() {}.getType());
+
+        assertTrue(result[0].isPresent());
+        assertEquals(1, result[0].getAsInt());
+
+        assertTrue(result[1].isPresent());
+        assertEquals(2, result[1].getAsInt());
+
+        assertEquals(OptionalInt.empty(), result[2]);
+    }
+
+    @Test
+    public void testMarshallOptionalArray() {
+        final Optional[] array = {Optional.of(new Customer(1, "Cust1")), Optional.of(new Customer(2, "Cust2")), Optional.empty()};
+        assertEquals("[{\"id\":1,\"name\":\"Cust1\"},{\"id\":2,\"name\":\"Cust2\"},null]", bindingJsonb.toJson(array));
+    }
+
+    @Test
+    public void testUnmarshallNullAsOptionalEmpty() {
+        final ScalarValueWrapper<OptionalInt> result = bindingJsonb.fromJson("{\"value\":null}", new ScalarValueWrapper<OptionalInt>() {
+        }.getClass().getGenericSuperclass());
+        assertEquals(OptionalInt.empty(), result.getValue());
+    }
+
+    @Test
+    public void testUnmarshallOptionalArrayNulls() {
+        final OptionalLong[] result = bindingJsonb.fromJson("[null, null]", OptionalLong[].class);
+
+        assertEquals(2, result.length);
+
+        for (OptionalLong item : result) {
+            assertEquals(OptionalLong.empty(), item);
+        }
+    }
+
+    @Test
+    public void testUnmarshallOptionalList() {
+        final List<Optional<Integer>> result = bindingJsonb.fromJson("[null, null]", new TestTypeToken<List<Optional<Integer>>>() {}.getType());
+
+        assertEquals(2, result.size());
+
+        for (Optional<Integer> item : result) {
+            assertEquals(Optional.empty(), item);
+        }
+    }
+
+    @Test
+    public void testMarshallOptionalMap() {
+        Map<String, OptionalInt> ints = new HashMap<>();
+        ints.put("first", OptionalInt.empty());
+        ints.put("second", OptionalInt.empty());
+        String result = defaultJsonb.toJson(ints);
+        assertEquals("{\"first\":null,\"second\":null}", result);
+    }
+
+    @Test
+    public void testCorrectOptionalGetter() {
+        NotMatchingGettersAndSetters personWithCorrectGetter = new NotMatchingGettersAndSetters();
+        String result = defaultJsonb.toJson(personWithCorrectGetter);
+        assertEquals("{\"firstName\":1,\"lastName\":\"Correct\"}", result);
+
+        NotMatchingGettersAndSetters deserialized = defaultJsonb.fromJson(result, NotMatchingGettersAndSetters.class);
+        personWithCorrectGetter.setFirstName(1);
+        assertEquals(personWithCorrectGetter, deserialized);
+    }
+
+    @Test
+    public void testMarshalEmptyRoot() {
+        assertEquals("null", bindingJsonb.toJson(Optional.empty()));
+    }
+
+    @Test
+    public void testUnmarshalEmptyRoot() {
+        assertEquals(Optional.empty(), bindingJsonb.fromJson("null", new TestTypeToken<Optional<Customer>>() {}.getType()));
+    }
+
+    @Test
+    public void testMarshalEmptyInt() {
+        assertEquals("null", bindingJsonb.toJson(OptionalInt.empty()));
+    }
+
+    @Test
+    public void testUnmarshalEmptyInt() {
+        assertEquals(OptionalInt.empty(), bindingJsonb.fromJson("null", OptionalInt.class));
+    }
+
+    @Test
+    public void testMarshalEmptyLong() {
+        assertEquals("null", bindingJsonb.toJson(OptionalLong.empty()));
+    }
+
+    @Test
+    public void testUnmarshalEmptyLong() {
+        assertEquals(OptionalLong.empty(), bindingJsonb.fromJson("null", OptionalLong.class));
+    }
+
+    @Test
+    public void testMarshalEmptyDouble() {
+        assertEquals("null", bindingJsonb.toJson(OptionalDouble.empty()));
+    }
+
+    @Test
+    public void testUnmarshalEmptyDouble() {
+        assertEquals(OptionalDouble.empty(), bindingJsonb.fromJson("null", OptionalDouble.class));
+    }
+
+    @Test
+    public void testNullInsteadOfOptional() {
+        OptionalWrapper optionalWrapper = new OptionalWrapper();
+        String expected = "{}";
+        assertThat(bindingJsonb.toJson(optionalWrapper), is(expected));
+    }
+
+    @Test
+    public void testNullInsteadOfOptionalInSpecificOptionals() {
+        SpecificOptionalWrapper optionalWrapper = new SpecificOptionalWrapper();
+        String expected = "{}";
+        assertThat(bindingJsonb.toJson(optionalWrapper), is(expected));
+    }
+
+    public static class Customer {
+        private int id;
+        private String name;
+
+        public Customer() {
+        }
+
+        public Customer(int id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/RecursiveReferenceTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/RecursiveReferenceTest.java
new file mode 100644
index 0000000..b70ae39
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/RecursiveReferenceTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Arrays;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.Jsonbs;
+import org.eclipse.yasson.adapters.model.Chain;
+import org.eclipse.yasson.adapters.model.ChainAdapter;
+import org.eclipse.yasson.adapters.model.ChainSerializer;
+import org.eclipse.yasson.adapters.model.Foo;
+import org.eclipse.yasson.adapters.model.FooAdapter;
+import org.eclipse.yasson.adapters.model.FooSerializer;
+import org.junit.jupiter.api.Test;
+
+public class RecursiveReferenceTest {
+
+    private static final Jsonb userSerializerJsonb = JsonbBuilder.create(new JsonbConfig()
+                                                                                 .withSerializers(new ChainSerializer(),
+                                                                                                  new FooSerializer()));
+    private static final Jsonb adapterSerializerJsonb = JsonbBuilder.create(new JsonbConfig()
+                                                                                    .withAdapters(new ChainAdapter(),
+                                                                                                  new FooAdapter()));
+
+    @Test
+    public void testSerializeRecursiveReference() {
+        Chain recursive = new Chain("test");
+        recursive.setLinksTo(recursive);
+        try {
+            Jsonbs.defaultJsonb.toJson(recursive);
+            fail("Exception should be caught");
+        } catch (JsonbException e) {
+            assertEquals(
+                    "Unable to serialize property 'linksTo' from org.eclipse.yasson.adapters.model.Chain",
+                    e.getMessage());
+            assertEquals(
+                    "Recursive reference has been found in class class org.eclipse.yasson.adapters.model.Chain.",
+                    e.getCause().getMessage());
+        }
+    }
+
+    @Test
+    public void testSerializeRecursiveReferenceCustomAdapter() {
+        Chain recursive = new Chain("test");
+        recursive.setLinksTo(recursive);
+        try {
+            adapterSerializerJsonb.toJson(recursive);
+            fail("Exception should be caught");
+        } catch (JsonbException e) {
+            assertEquals("Problem adapting object of type class org.eclipse.yasson.adapters.model.Chain to java.util.Map<java.lang"
+                            + ".String, java.lang.Object> in class class org.eclipse.yasson.adapters.model.ChainAdapter",
+                    e.getMessage());
+        }
+    }
+
+    @Test
+    public void testSerializeRecursiveReferenceCustomSerializer() {
+        Chain recursive = new Chain("test");
+        recursive.setLinksTo(recursive);
+        try {
+            userSerializerJsonb.toJson(recursive);
+            fail("Exception should be caught");
+        } catch (JsonbException e) {
+            assertEquals("Recursive reference has been found in class class org.eclipse.yasson.adapters.model.Chain.",
+                         e.getMessage());
+        }
+    }
+
+    @Test
+    public void testSerializeRepeatedInstance() {
+        String noNulls = "[{\"linksTo\":{\"name\":\"test\"},\"name\":\"test\"},{\"linksTo\":{\"name\":\"test\"},"
+                + "\"name\":\"test\"}]";
+        String withNulls = "[{\"has\":null,\"linksTo\":{\"has\":null,\"linksTo\":null,\"name\":\"test\"},\"name\":\"test\"},"
+                + "{\"has\":null,\"linksTo\":{\"has\":null,\"linksTo\":null,\"name\":\"test\"},\"name\":\"test\"}]";
+        checkSerializeRepeatedInstance(Jsonbs.defaultJsonb, noNulls);
+        //Since ChainAdapter is adapting Chain to Map<String, Object>, the produced json will contain nulls
+        checkSerializeRepeatedInstance(adapterSerializerJsonb, withNulls);
+        checkSerializeRepeatedInstance(userSerializerJsonb, noNulls);
+    }
+
+    private void checkSerializeRepeatedInstance(Jsonb jsonb, String expected) {
+        Chain recursive = new Chain("test");
+        recursive.setLinksTo(new Chain("test"));
+        String result = jsonb.toJson(Arrays.asList(recursive, recursive));
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testSerialize2ReferencesSameObject() {
+        A a = new A();
+        Foo b = new Foo("foo");
+        a.ref1 = b;
+        a.ref2 = b;
+        String result = Jsonbs.defaultJsonb.toJson(a);
+        assertEquals("{\"ref1\":{\"bar\":\"foo\"},\"ref2\":{\"bar\":\"foo\"}}", result);
+    }
+
+    @Test
+    public void testChain() {
+        String noNulls = "{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"name\":\"c2\"},\"name\":\"c1\"}";
+        String withNulls = "{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"linksTo\":null,"
+                + "\"name\":\"c2\"},\"name\":\"c1\"}";
+        checkChain(Jsonbs.defaultJsonb, noNulls);
+        //Since ChainAdapter is adapting Chain to Map<String, Object>, the produced json will contain nulls
+        checkChain(adapterSerializerJsonb, withNulls);
+        checkChain(userSerializerJsonb, noNulls);
+    }
+
+    private void checkChain(Jsonb jsonb, String expected) {
+        Foo foo = new Foo("foo");
+        Chain c1 = new Chain("c1");
+        Chain c2 = new Chain("c2");
+        c1.setLinksTo(c2);
+        c1.setHas(foo);
+        c2.setHas(foo);
+        String result = jsonb.toJson(c1);
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testDeeperChain() {
+        String noNulls = "{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"name\":\"c3\"},"
+                + "\"name\":\"c2\"},\"name\":\"c1\"}";
+        String withNulls = "{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":null,"
+                + "\"linksTo\":null,\"name\":\"c3\"},\"name\":\"c2\"},\"name\":\"c1\"}";
+        checkDeeperChain(Jsonbs.defaultJsonb, noNulls);
+        //Since ChainAdapter is adapting Chain to Map<String, Object>, the produced json will contain nulls
+        checkDeeperChain(adapterSerializerJsonb, withNulls);
+        checkDeeperChain(userSerializerJsonb, noNulls);
+    }
+
+    private void checkDeeperChain(Jsonb jsonb, String expected) {
+        Foo foo = new Foo("foo");
+        Chain c1 = new Chain("c1");
+        Chain c2 = new Chain("c2");
+        Chain c3 = new Chain("c3");
+        c1.setLinksTo(c2);
+        c1.setHas(foo);
+        c2.setHas(foo);
+        c2.setLinksTo(c3);
+        String result = jsonb.toJson(c1);
+        assertEquals(expected, result);
+    }
+
+    public static class A {
+        public Foo ref1;
+        public Foo ref2;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/SpecificTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/SpecificTest.java
new file mode 100644
index 0000000..bb19fb1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/SpecificTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+/**
+ * Specific standard Java SE types tests: {@link BigDecimal}, {@link BigInteger}, {@link URL}, {@link URI}.
+ *
+ * @author Dmitry Kornilov
+ */
+public class SpecificTest {
+    @Test
+    public void testMarshallBigDecimal() {
+        assertEquals("{\"value\":100}", bindingJsonb.toJson(new ScalarValueWrapper<>(BigDecimal.valueOf(100L))));
+        assertEquals("{\"value\":100.1}", bindingJsonb.toJson(new ScalarValueWrapper<>(BigDecimal.valueOf(100.1D))));
+    }
+
+    @Test
+    public void testMarshallBigInteger() {
+        assertEquals("{\"value\":100}", bindingJsonb.toJson(new ScalarValueWrapper<>(BigInteger.valueOf(100))));
+    }
+
+    @Test
+    public void testMarshallUri() throws URISyntaxException {
+        assertEquals("{\"value\":\"http://www.oracle.com\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(new URI("http://www.oracle.com"))));
+    }
+
+    @Test
+    public void testMarshallUrl() throws MalformedURLException {
+        assertEquals("{\"value\":\"http://www.oracle.com\"}", bindingJsonb.toJson(new ScalarValueWrapper<>(new URL("http://www.oracle.com"))));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/UnmarshallingUnsupportedTypesTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/UnmarshallingUnsupportedTypesTest.java
new file mode 100644
index 0000000..4963b12
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/UnmarshallingUnsupportedTypesTest.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.generics.model.GenericTestClass;
+import org.eclipse.yasson.defaultmapping.specific.model.ClassWithUnsupportedFields;
+import org.eclipse.yasson.defaultmapping.specific.model.CustomUnsupportedInterface;
+import org.eclipse.yasson.defaultmapping.specific.model.SupportedTypes;
+import org.eclipse.yasson.defaultmapping.specific.model.SupportedTypes.NestedPojo;
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.eclipse.yasson.YassonConfig.FAIL_ON_UNKNOWN_PROPERTIES;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class UnmarshallingUnsupportedTypesTest {
+
+    @Test
+    public void testUnmarshallToUnsupportedInterface() {
+        ClassWithUnsupportedFields unsupported = new ClassWithUnsupportedFields();
+        unsupported.customInterface = new CustomUnsupportedInterface() {
+
+            private String value = "value1";
+            @Override
+            public String getValue() {
+                return value;
+            }
+
+            @Override
+            public void setValue(String value) {
+                throw new IllegalStateException("Not supposed to be called.");
+            }
+        };
+        String expected = "{\"customInterface\":{\"value\":\"value1\"}}";
+        assertEquals(expected, defaultJsonb.toJson(unsupported));
+        try {
+        	defaultJsonb.fromJson(expected, ClassWithUnsupportedFields.class);
+            fail("Should report an error");
+        } catch (JsonbException e) {
+            assertTrue(e.getMessage().contains("Cannot infer a type"));
+            assertTrue(e.getMessage().contains("customInterface"));
+        }
+    }
+
+    @Test
+    public void testPojoForMalformedJson() {
+
+        SupportedTypes supportedTypes = new SupportedTypes();
+        LocalDateTime localDateTime = LocalDateTime.of(2015, 12, 28, 15, 57);
+        ZoneId prague = ZoneId.of("Europe/Prague");
+        ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, prague);
+        supportedTypes.setInstant(Instant.from(zonedDateTime));
+        supportedTypes.setOptionalLong(OptionalLong.of(11L));
+        supportedTypes.setZonedDateTime(zonedDateTime);
+        supportedTypes.setNestedPojo(new SupportedTypes.NestedPojo());
+        supportedTypes.getNestedPojo().setIntegerValue(10);
+
+        String json = "{\"instant\":\"2015-12-28T14:57:00Z\",\"nestedPojo\":{\"integerValue\":10},\"optionalLong\":11,\"zonedDateTime\":\"2015-12-28T15:57:00+01:00[Europe/Prague]\"}";
+        assertEquals(json, defaultJsonb.toJson(supportedTypes));
+
+        SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class);
+        assertEquals(result.getInstant(), supportedTypes.getInstant());
+        assertEquals(result.getZonedDateTime(), supportedTypes.getZonedDateTime());
+        assertEquals(result.getOptionalLong(), supportedTypes.getOptionalLong());
+        assertEquals(Integer.valueOf(10), result.getNestedPojo().getIntegerValue());
+
+    }
+
+    @Test
+    public void testPojoAsScalarValue() {
+        //wrong, nestedPojo is a value.
+        String json  = "{\"nestedPojo\":\"10\",\"optionalLong\":11}";
+        assertFail(json, SupportedTypes.class, "nestedPojo", NestedPojo.class);
+    }
+
+    @Test
+    public void testPojoAsArray() {
+        //wrong, nestedPojo is a collection.
+        String json  = "{\"nestedPojo\":[\"10\"],\"optionalLong\":11}";
+        assertFail(json, SupportedTypes.class, "nestedPojo", NestedPojo.class);
+    }
+
+    @Test()
+    public void testMissingFieldDefault() {
+        String json  = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":5},\"optionalLong\":11}";
+        SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class);
+        assertEquals(Integer.valueOf(10), result.getNestedPojo().getIntegerValue());
+        assertEquals(11, result.getOptionalLong().getAsLong());
+    }
+
+    @Test()
+    public void testMissingFieldDefaultNull() {
+        String json  = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":null},\"optionalLong\":11}";
+        SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class);
+        assertEquals(Integer.valueOf(10), result.getNestedPojo().getIntegerValue());
+        assertEquals(11, result.getOptionalLong().getAsLong());
+    }
+
+    @Test
+    public void testMissingFieldIgnored() {
+    	assertThrows(JsonbException.class, () -> {
+	        Jsonb defaultConfig = JsonbBuilder.create(new JsonbConfig().setProperty(FAIL_ON_UNKNOWN_PROPERTIES, true));
+	        String json  = "{\"nestedPojo\":{\"integerValue\":10,\"missingField\":5},\"optionalLong\":11}";
+	        SupportedTypes result = defaultConfig.fromJson(json, SupportedTypes.class);
+    	});
+    }
+
+    @Test
+    public void testMissingFieldWithObjectValue() {
+        String json  = "{\"missingProperty\":{\"optionalLong\":404},\"optionalLong\":11}";
+        SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class);
+
+        assertTrue(result.getOptionalLong().isPresent());
+        assertThat(result.getOptionalLong().getAsLong(), is(11L));
+    }
+
+    @Test
+    public void testMissingFieldWithArrayValue() {
+        String json  = "{\"missingProperty\":[404],\"optionalLong\":11}";
+        SupportedTypes result = defaultJsonb.fromJson(json, SupportedTypes.class);
+
+        assertTrue(result.getOptionalLong().isPresent());
+        assertThat(result.getOptionalLong().getAsLong(), is(11L));
+    }
+
+    @Test
+    public void testEmptyStringAsInteger() {
+        Type type = new TestTypeToken<GenericTestClass<Integer, Integer>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type, "field1", Integer.class);
+    }
+
+    @Test
+    public void testEmptyStringAsDouble() {
+        Type type = new TestTypeToken<GenericTestClass<Double, Double>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type, "field1", Double.class);
+    }
+
+    @Test
+    public void testEmptyStringAsFloat() {
+        Type type = new TestTypeToken<GenericTestClass<Float, Float>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type, "field1", Float.class);
+    }
+
+    @Test
+    public void testEmptyStringAsLong() {
+        Type type = new TestTypeToken<GenericTestClass<Long, Long>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type, "field1", Long.class);
+    }
+
+    @Test
+    public void testEmptyStringAsShort() {
+        Type type = new TestTypeToken<GenericTestClass<Short, Short>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type, "field1", Short.class);
+    }
+
+    @Test
+    public void testEmptyStringAsByte() {
+        Type type = new TestTypeToken<GenericTestClass<Byte, Byte>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type, "field1", Byte.class);
+    }
+
+    @Test
+    public void testEmptyStringAsBigDecimal() {
+        Type type = new TestTypeToken<GenericTestClass<BigDecimal, BigDecimal>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type,"field1", BigDecimal.class);
+    }
+
+    @Test
+    public void testEmptyStringAsBigInteger() {
+        Type type = new TestTypeToken<GenericTestClass<BigInteger, BigInteger>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type, "field1", BigInteger.class);
+    }
+
+    @Test
+    public void testEmptyStringAsOptionalDouble() {
+        Type type = new TestTypeToken<GenericTestClass<OptionalDouble, OptionalDouble>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type,"field1", Double.class); //We are reusing Double deserializer
+    }
+
+    @Test
+    public void testEmptyStringAsOptionalInt() {
+        Type type = new TestTypeToken<GenericTestClass<OptionalInt, OptionalInt>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type, "field1", Integer.class); //We are reusing Integer deserializer
+    }
+
+    @Test
+    public void testEmptyStringAsOptionalLong() {
+        Type type = new TestTypeToken<GenericTestClass<OptionalLong, OptionalLong>>(){}.getType();
+        assertFail("{\"field1\":\"\"}", type,"field1", Long.class); //We are reusing Long deserializer
+    }
+
+    private void assertFail(String json, Type type, String failureProperty, Class<?> failurePropertyClass) {
+        try {
+        	defaultJsonb.fromJson(json, type);
+            fail();
+        } catch (JsonbException e) {
+            if(!e.getMessage().contains(failureProperty) || !e.getMessage().contains(failurePropertyClass.getName())) {
+                fail("Expected error message to contain '" + failureProperty + "' and '" + failurePropertyClass.getName() + "', but was: " +
+                 e.getMessage());
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Address.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Address.java
new file mode 100644
index 0000000..c04dadf
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Address.java
@@ -0,0 +1,46 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class Address {
+    private Street street;
+
+    private String town;
+
+    public Address() {
+    }
+
+    public Address(Street street, String town) {
+        this.street = street;
+        this.town = town;
+    }
+
+    public Street getStreet() {
+        return street;
+    }
+
+    public void setStreet(Street street) {
+        this.street = street;
+    }
+
+    public String getTown() {
+        return town;
+    }
+
+    public void setTown(String town) {
+        this.town = town;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/ClassWithUnsupportedFields.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/ClassWithUnsupportedFields.java
new file mode 100644
index 0000000..20695e0
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/ClassWithUnsupportedFields.java
@@ -0,0 +1,20 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ClassWithUnsupportedFields {
+    public CustomUnsupportedInterface customInterface;
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/CustomUnsupportedInterface.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/CustomUnsupportedInterface.java
new file mode 100644
index 0000000..2c1c59a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/CustomUnsupportedInterface.java
@@ -0,0 +1,25 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+/**
+ * Unmarhsalling to this interface is not supported.
+ *
+ * @author Roman Grigoriadi
+ */
+public interface CustomUnsupportedInterface {
+
+    String getValue();
+
+    void setValue(String value);
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Customer.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Customer.java
new file mode 100644
index 0000000..98949d8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Customer.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Just a dummy pojo object, for testing object tree encapsulation.
+ * Not much business sense here to look for.
+ *
+ * @author Roman Grigoriadi
+ */
+public class Customer {
+
+    private List<Address> addresses;
+
+    private List<String> strings;
+
+    private List<Integer> integers;
+
+    private List<List<Integer>> listOfListsOfIntegers;
+
+    private Map<String, Integer> stringIntegerMap;
+
+    private Map<String, Customer> friends;
+
+    private Integer age;
+
+    private String name;
+
+    public Customer() {
+    }
+
+    public Customer(Integer age, String name) {
+        this.age = age;
+        this.name = name;
+    }
+
+    public List<Address> getAddresses() {
+        return addresses;
+    }
+
+    public void setAddresses(List<Address> addresses) {
+        this.addresses = addresses;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<String> getStrings() {
+        return strings;
+    }
+
+    public void setStrings(List<String> strings) {
+        this.strings = strings;
+    }
+
+    public List<Integer> getIntegers() {
+        return integers;
+    }
+
+    public void setIntegers(List<Integer> integers) {
+        this.integers = integers;
+    }
+
+    public Map<String, Integer> getStringIntegerMap() {
+        return stringIntegerMap;
+    }
+
+    public void setStringIntegerMap(Map<String, Integer> stringIntegerMap) {
+        this.stringIntegerMap = stringIntegerMap;
+    }
+
+    public Map<String, Customer> getFriends() {
+        return friends;
+    }
+
+    public void setFriends(Map<String, Customer> friends) {
+        this.friends = friends;
+    }
+
+    public List<List<Integer>> getListOfListsOfIntegers() {
+        return listOfListsOfIntegers;
+    }
+
+    public void setListOfListsOfIntegers(List<List<Integer>> listOfListsOfIntegers) {
+        this.listOfListsOfIntegers = listOfListsOfIntegers;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/NotMatchingGettersAndSetters.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/NotMatchingGettersAndSetters.java
new file mode 100644
index 0000000..7b36fe5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/NotMatchingGettersAndSetters.java
@@ -0,0 +1,55 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * @author David Kral.
+ */
+public class NotMatchingGettersAndSetters {
+
+    private String firstName = "Person";
+    private String lastName = "Correct";
+
+    public void setFirstName(Integer firstName) {
+        this.firstName = Integer.toString(firstName);
+    }
+
+    public Optional<Integer> getFirstName() {
+        return Optional.of(1);
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public Optional<String> getLastName() {
+        return Optional.ofNullable(lastName);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NotMatchingGettersAndSetters that = (NotMatchingGettersAndSetters) o;
+        return Objects.equals(firstName, that.firstName) &&
+                Objects.equals(lastName, that.lastName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(firstName, lastName);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/OptionalWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/OptionalWrapper.java
new file mode 100644
index 0000000..1a04f5b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/OptionalWrapper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+import java.util.Optional;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class OptionalWrapper {
+
+    private Optional<Street> streetOptional;
+
+    public Optional<Street> getStreetOptional() {
+        return streetOptional;
+    }
+
+    public void setStreetOptional(Optional<Street> streetOptional) {
+        this.streetOptional = streetOptional;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/SpecificOptionalWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/SpecificOptionalWrapper.java
new file mode 100644
index 0000000..b903ad5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/SpecificOptionalWrapper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+
+public class SpecificOptionalWrapper {
+
+    private OptionalInt optionalInt;
+    private OptionalLong optionalLong;
+    private OptionalDouble optionalDouble;
+
+    public OptionalInt optionalInt() {
+        return optionalInt;
+    }
+
+    public void setOptionalInt(OptionalInt optionalInt) {
+        this.optionalInt = optionalInt;
+    }
+
+    public OptionalLong optionalLong() {
+        return optionalLong;
+    }
+
+    public void setOptionalLong(OptionalLong optionalLong) {
+        this.optionalLong = optionalLong;
+    }
+
+    public OptionalDouble optionalDouble() {
+        return optionalDouble;
+    }
+
+    public void setOptionalDouble(OptionalDouble optionalDouble) {
+        this.optionalDouble = optionalDouble;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Street.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Street.java
new file mode 100644
index 0000000..7037a11
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/Street.java
@@ -0,0 +1,45 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class Street {
+    private String name = "defaultName";
+    private Integer number = 11;
+
+    public Street() {
+    }
+
+    public Street(String name, Integer number) {
+        this.name = name;
+        this.number = number;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/StreetWithPrimitives.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/StreetWithPrimitives.java
new file mode 100644
index 0000000..fe4c872
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/StreetWithPrimitives.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class StreetWithPrimitives {
+    private String name = "defaultName";
+    private int number = 11;
+
+    public StreetWithPrimitives() {
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getNumber() {
+        return number;
+    }
+
+    public void setNumber(int number) {
+        this.number = number;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/SupportedTypes.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/SupportedTypes.java
new file mode 100644
index 0000000..1c2edf6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/model/SupportedTypes.java
@@ -0,0 +1,77 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.specific.model;
+
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.util.OptionalLong;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class SupportedTypes {
+
+    public static class NestedPojo {
+
+        private Integer integerValue;
+
+        public Integer getIntegerValue() {
+            return integerValue;
+        }
+
+        public void setIntegerValue(Integer integerValue) {
+            this.integerValue = integerValue;
+        }
+
+    }
+
+    private Instant instant;
+
+    private ZonedDateTime zonedDateTime;
+
+    private OptionalLong optionalLong;
+
+    private NestedPojo nestedPojo;
+
+    public Instant getInstant() {
+        return instant;
+    }
+
+    public void setInstant(Instant instant) {
+        this.instant = instant;
+    }
+
+    public OptionalLong getOptionalLong() {
+        return optionalLong;
+    }
+
+    public void setOptionalLong(OptionalLong optionalLong) {
+        this.optionalLong = optionalLong;
+    }
+
+    public ZonedDateTime getZonedDateTime() {
+        return zonedDateTime;
+    }
+
+    public void setZonedDateTime(ZonedDateTime zonedDateTime) {
+        this.zonedDateTime = zonedDateTime;
+    }
+
+    public NestedPojo getNestedPojo() {
+        return nestedPojo;
+    }
+
+    public void setNestedPojo(NestedPojo nestedPojo) {
+        this.nestedPojo = nestedPojo;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/DefaultSerializersTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/DefaultSerializersTest.java
new file mode 100644
index 0000000..3527549
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/DefaultSerializersTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.typeConvertors;
+
+import org.junit.jupiter.api.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.defaultmapping.generics.model.ScalarValueWrapper;
+import org.eclipse.yasson.defaultmapping.typeConvertors.model.ByteArrayWrapper;
+import org.eclipse.yasson.internal.JsonBindingBuilder;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.config.BinaryDataStrategy;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Base64;
+import java.util.UUID;
+
+/**
+ * This class contains Converter tests
+ *
+ * @author David Kral
+ */
+public class DefaultSerializersTest {
+
+    @Test
+    public void testCharacter() {
+        final String json = "{\"value\":\"\uFFFF\"}";
+        assertEquals(json, defaultJsonb.toJson(new ScalarValueWrapper<>('\uFFFF')));
+        ScalarValueWrapper<Character> result = defaultJsonb.fromJson(json, new TestTypeToken<ScalarValueWrapper<Character>>(){}.getType());
+        assertEquals((Character)'\uFFFF', result.getValue());
+    }
+
+    @Test
+    public void testByteArray() {
+        byte[] array = {1, 2, 3};
+        assertEquals("[1,2,3]", bindingJsonb.toJson(array));
+    }
+
+    @Test
+    public void testByteArrayWithBinaryStrategy() {
+        byte[] array = {127, -128, 127};
+        Jsonb jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withBinaryDataStrategy(BinaryDataStrategy.BYTE))).build();
+
+        assertEquals("[127,-128,127]", jsonb.toJson(array));
+        assertArrayEquals(array, jsonb.fromJson("[127,-128,127]", byte[].class));
+    }
+
+    @Test
+    public void testByteArrayWithStrictJson() {
+        byte[] array = {1, 2, 3};
+        ByteArrayWrapper byteArrayWrapper = new ByteArrayWrapper();
+        byteArrayWrapper.array = array;
+        Jsonb jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true))).build();
+
+        assertEquals("{\"array\":\"" + Base64.getUrlEncoder().encodeToString(array) + "\"}", jsonb.toJson(byteArrayWrapper));
+
+        jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(false))).build();
+
+        assertEquals("{\"array\":[1,2,3]}", jsonb.toJson(byteArrayWrapper));
+    }
+
+    @Test
+    public void testByteArrayWithStrictJsonAndBinaryStrategy() {
+        byte[] array = {1, 2, 3};
+        ByteArrayWrapper byteArrayWrapper = new ByteArrayWrapper();
+        byteArrayWrapper.array = array;
+        Jsonb jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true).withBinaryDataStrategy(BinaryDataStrategy.BYTE))).build();
+        final String base64UrlEncodedJson = "{\"array\":\"" + Base64.getUrlEncoder().encodeToString(array) + "\"}";
+        assertEquals(base64UrlEncodedJson, jsonb.toJson(byteArrayWrapper));
+        ByteArrayWrapper result = jsonb.fromJson(base64UrlEncodedJson, ByteArrayWrapper.class);
+        assertArrayEquals(array, result.array);
+
+        jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true).withBinaryDataStrategy(BinaryDataStrategy.BASE_64))).build();
+        assertEquals(base64UrlEncodedJson, jsonb.toJson(byteArrayWrapper));
+        result = jsonb.fromJson(base64UrlEncodedJson, ByteArrayWrapper.class);
+        assertArrayEquals(array, result.array);
+
+        jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withStrictIJSON(true).withBinaryDataStrategy(BinaryDataStrategy.BASE_64_URL))).build();
+        assertEquals(base64UrlEncodedJson, jsonb.toJson(byteArrayWrapper));
+        result = jsonb.fromJson(base64UrlEncodedJson, ByteArrayWrapper.class);
+        assertArrayEquals(array, result.array);
+
+        jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withBinaryDataStrategy(BinaryDataStrategy.BYTE))).build();
+        assertEquals("[1,2,3]", jsonb.toJson(array));
+        result = jsonb.fromJson("{\"array\":[1,2,3]}", ByteArrayWrapper.class);
+        assertArrayEquals(array, result.array);
+
+        jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withBinaryDataStrategy(BinaryDataStrategy.BASE_64))).build();
+        final String base64EncodedJson = "{\"array\":\"" + Base64.getEncoder().encodeToString(array) + "\"}";
+        assertEquals(base64EncodedJson, jsonb.toJson(byteArrayWrapper));
+        result = jsonb.fromJson(base64EncodedJson, ByteArrayWrapper.class);
+        assertArrayEquals(array, result.array);
+
+        jsonb = (new JsonBindingBuilder().withConfig(new JsonbConfig().withBinaryDataStrategy(BinaryDataStrategy.BASE_64_URL))).build();
+        assertEquals(base64UrlEncodedJson, jsonb.toJson(byteArrayWrapper));
+        result = jsonb.fromJson(base64UrlEncodedJson, ByteArrayWrapper.class);
+        assertArrayEquals(array, result.array);
+    }
+    
+    @Test
+    public void testUUID() {
+        UUID uuid = UUID.randomUUID();
+        String json = defaultJsonb.toJson(uuid);
+        UUID result = defaultJsonb.fromJson(json, UUID.class);
+        assertEquals(uuid, result);
+    }
+    
+    @Test
+    public void serializeObjectWithPth() {
+        
+        Path expectedPath = Paths.get("/tmp/hello/me.txt");
+        String expectedPathString = expectedPath.toString().replace("\\", "\\\\");        
+        String expectedJson = "{\"path\":\"" + expectedPathString + "\"}";
+        final ObjectWithPath objectWithPath = new ObjectWithPath();
+        objectWithPath.path = expectedPath;
+        final String s = defaultJsonb.toJson(objectWithPath);
+        assertEquals(expectedJson, s);
+        
+        ObjectWithPath actualObject = defaultJsonb.fromJson(expectedJson, ObjectWithPath.class);
+        assertEquals(expectedPath, actualObject.path);
+    }
+    
+    public static class ObjectWithPath {
+        public Path path;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/BigDecimalWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/BigDecimalWrapper.java
new file mode 100644
index 0000000..eadb209
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/BigDecimalWrapper.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.typeConvertors.model;
+
+import java.math.BigDecimal;
+
+/**
+ * Created by David Kral.
+ */
+public class BigDecimalWrapper {
+
+    public BigDecimal bigDecimal;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/BigIntegerWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/BigIntegerWrapper.java
new file mode 100644
index 0000000..b7404cb
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/BigIntegerWrapper.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.typeConvertors.model;
+
+import java.math.BigInteger;
+
+/**
+ * Created by David Kral.
+ */
+public class BigIntegerWrapper {
+
+    public BigInteger bigInteger;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/ByteArrayWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/ByteArrayWrapper.java
new file mode 100644
index 0000000..fbdc47b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/ByteArrayWrapper.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.typeConvertors.model;
+
+/**
+ * Created by David Kral.
+ */
+public class ByteArrayWrapper {
+
+    public byte[] array;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/CalendarWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/CalendarWrapper.java
new file mode 100644
index 0000000..60d193d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/CalendarWrapper.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.typeConvertors.model;
+
+import java.util.Calendar;
+
+/**
+ * Created by David Kral.
+ */
+public class CalendarWrapper {
+
+    public Calendar calendar;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/StringWrapper.java b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/StringWrapper.java
new file mode 100644
index 0000000..8c91759
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/typeConvertors/model/StringWrapper.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.defaultmapping.typeConvertors.model;
+
+/**
+ * Created by David Kral.
+ */
+public class StringWrapper {
+
+    public String string;
+
+}
diff --git a/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java b/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java
new file mode 100644
index 0000000..d49e4a0
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/documented/DocumentationExampleTest.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.documented;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.bind.annotation.JsonbNillable;
+import jakarta.json.bind.annotation.JsonbNumberFormat;
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTransient;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+
+/**
+ * Contains tests from http://json-b.net/docs/user-guide.html
+ */
+public class DocumentationExampleTest {
+
+    public static class Dog {
+        public String name;
+        public int age;
+        public boolean bitable;
+    }
+
+    @Test
+    public void testMappingExample() {
+        // Create a dog instance
+        Dog dog = new Dog();
+        dog.name = "Falco";
+        dog.age = 4;
+        dog.bitable = false;
+
+        // Create Jsonb and serialize
+        String result = defaultJsonb.toJson(dog);
+        assertEquals("{\"age\":4,\"bitable\":false,\"name\":\"Falco\"}", result);
+
+        // Deserialize back
+        dog = defaultJsonb.fromJson("{\"name\":\"Falco\",\"age\":4,\"bites\":false}", Dog.class);
+        assertEquals("Falco", dog.name);
+        assertEquals(4, dog.age);
+        assertEquals(false, dog.bitable);
+    }
+
+    @Test
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testMappingCollection() {
+        Dog falco = new Dog();
+        falco.name = "Falco";
+        falco.age = 4;
+        Dog cassidy = new Dog();
+        cassidy.name = "Cassidy";
+        cassidy.age = 5;
+
+        // List of dogs
+        List dogs = new ArrayList();
+        dogs.add(falco);
+        dogs.add(cassidy);
+
+        // Create Jsonb and serialize
+        String result = defaultJsonb.toJson(dogs);
+        assertEquals(
+                "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]",
+                result);
+
+        // We can also deserialize back into a raw collection, but since there is no way
+        // to infer a type here,
+        // the result will be a list of java.util.Map instances with string keys.
+        dogs = defaultJsonb.fromJson(result, ArrayList.class);
+        assertEquals(2, dogs.size());
+        assertEquals("Falco", ((Map) dogs.get(0)).get("name"));
+        assertEquals("Cassidy", ((Map) dogs.get(1)).get("name"));
+        // assertEquals(4, ((Map) dogs.get(0)).get("age")); // TODO should these
+        // actually be BigDecimals?
+        // assertEquals(5, ((Map) dogs.get(1)).get("age"));
+    }
+
+    @SuppressWarnings("serial")
+    @Test
+    public void testMappingGenericCollection() {
+        Dog falco = new Dog();
+        falco.name = "Falco";
+        falco.age = 4;
+        Dog cassidy = new Dog();
+        cassidy.name = "Cassidy";
+        cassidy.age = 5;
+
+        // List of dogs
+        List<Dog> dogs = new ArrayList<>();
+        dogs.add(falco);
+        dogs.add(cassidy);
+
+        // Create Jsonb and serialize
+        String result = defaultJsonb.toJson(dogs);
+        assertEquals(
+                "[{\"age\":4,\"bitable\":false,\"name\":\"Falco\"},{\"age\":5,\"bitable\":false,\"name\":\"Cassidy\"}]",
+                result);
+
+        // Deserialize back
+        dogs = defaultJsonb.fromJson(result, new ArrayList<Dog>() {
+        }.getClass().getGenericSuperclass());
+        assertEquals(2, dogs.size());
+        assertEquals("Falco", dogs.get(0).name);
+        assertEquals("Cassidy", dogs.get(1).name);
+    }
+
+    @Test
+    public void testFormattedOutput() {
+        Dog pojo = new Dog();
+        pojo.name = "Falco";
+        pojo.age = 4;
+
+        // Use it!
+        String result = formattingJsonb.toJson(pojo);
+        assertEquals("{\n" + 
+                "    \"age\": 4,\n" + 
+                "    \"bitable\": false,\n" + 
+                "    \"name\": \"Falco\"\n" + 
+                "}", result);
+    }
+    
+    public static class Person1 {
+        @JsonbProperty("person-name")
+        public String name;
+        public String profession;
+    }
+    
+    @Test
+    public void testChangingPropertyNames1() {
+        Person1 p = new Person1();
+        p.name = "Jason Bourne";
+        p.profession = "Super Agent";
+        
+        String result = formattingJsonb.toJson(p);
+        assertEquals("{\n" + 
+                "    \"person-name\": \"Jason Bourne\",\n" + 
+                "    \"profession\": \"Super Agent\"\n" + 
+                "}", result);
+    }
+    
+    public class Person2 {
+        private String name;
+        private String profession;
+
+        @JsonbProperty("person-name")
+        public String getName() {
+            return name;
+        }
+
+        public String getProfession() {
+            return profession;
+        }
+
+        // public setters ...
+        public void setName(String name) {
+            this.name = name;
+        }
+        public void setProfession(String profession) {
+            this.profession = profession;
+        }
+    }
+    
+    @Test
+    public void testChangingPropertyNames2() {
+        Person2 p = new Person2();
+        p.name = "Jason Bourne";
+        p.profession = "Super Agent";
+        
+        String result = formattingJsonb.toJson(p);
+        assertEquals("{\n" + 
+                "    \"person-name\": \"Jason Bourne\",\n" + 
+                "    \"profession\": \"Super Agent\"\n" + 
+                "}", result);
+    }
+    
+    public static class Person3 {
+        private String name;
+
+        @JsonbProperty("name-to-write")
+        public String getName() {
+            return name;
+        }
+
+        @JsonbProperty("name-to-read")
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+    
+    @Test
+    public void testChangingPropertyNames3() {
+        Person3 p = new Person3();
+        p.name = "Jason Bourne";
+        String result = defaultJsonb.toJson(p);
+        assertEquals("{\"name-to-write\":\"Jason Bourne\"}", result);
+        
+        String json = "{\"name-to-read\":\"Jason Bourne\"}";
+        Person3 after = defaultJsonb.fromJson(json, Person3.class);
+        assertEquals("Jason Bourne", after.name);
+    }
+    
+    public static class Person4 { // TODO: a non-static class results in an NPE
+        @JsonbTransient
+        private String name;
+
+        private String profession;
+
+        // public getters/setters ...
+        public String getName() {
+            return name;
+        }
+        public void setName(String name) {
+            this.name = name;
+        }
+        public String getProfession() {
+            return this.profession;
+        }
+        public void setProfession(String profession) {
+            this.profession = profession;
+        }
+    }
+    
+    @Test
+    public void testIgnoringProperties() {
+        Person4 p = new Person4();
+        p.name = "Jason Bourne";
+        p.profession = "Super Agent";
+        String result = defaultJsonb.toJson(p);
+        assertEquals("{\"profession\":\"Super Agent\"}", result);
+        
+        String json = "{\"profession\":\"Super Agent\"}";
+        Person4 after = defaultJsonb.fromJson(json, Person4.class);
+        assertEquals("Super Agent", after.profession);
+        assertNull(after.name);
+    }
+    
+    @JsonbNillable
+    public class Person5 {
+        private String name;
+        private String profession;
+
+        // public getters/setters ...
+        public String getName() {
+            return name;
+        }
+        public void setName(String name) {
+            this.name = name;
+        }
+        public String getProfession() {
+            return profession;
+        }
+        public void setProfession(String profession) {
+            this.profession = profession;
+        }
+    }
+    
+    @Test
+    public void testNullHandling1() {
+        Person5 p = new Person5();
+        String result = defaultJsonb.toJson(p);
+        assertEquals("{\"name\":null,\"profession\":null}", result);
+    }
+    
+    public class Person6 {
+        @JsonbProperty(nillable=true)
+        private String name;
+
+        private String profession;
+
+        // public getters/setters ...
+        public String getName() {
+            return name;
+        }
+        public void setName(String name) {
+            this.name = name;
+        }
+        public String getProfession() {
+            return profession;
+        }
+        public void setProfession(String profession) {
+            this.profession = profession;
+        }
+    }
+    
+    @Test
+    public void testNullHandling2() {
+        Person6 p = new Person6();
+        String result = defaultJsonb.toJson(p);
+        assertEquals("{\"name\":null}", result);
+    }
+    
+    public static class Person {
+        public String name;
+        public String profession;
+    }
+
+    @Test
+    public void testNullHandling3() {
+        Person p = new Person();
+        String result = nullableJsonb.toJson(p);
+        assertEquals("{\"name\":null,\"profession\":null}", result);
+    }
+    
+    public static class Person8 { // TODO: obscure error here if non-static
+        public final String name;
+        public String profession;
+
+        @JsonbCreator
+        public Person8(@JsonbProperty("name") String name) {
+            this.name = name;
+        }
+    }
+    
+    @Test
+    public void testCustomInstantiation() {
+        Person8 p = defaultJsonb.fromJson("{\"name\":\"Jason Bourne\"}", Person8.class);
+        assertEquals("Jason Bourne", p.name);
+    }
+    
+    public static class Person9 {
+        public String name;
+
+        @JsonbDateFormat("dd.MM.yyyy")
+        public LocalDate birthDate;
+
+        @JsonbNumberFormat("#0.00")
+        public BigDecimal salary;
+    }
+    
+    @Test
+    public void testDateNumberFormats1() {
+        Person9 p = new Person9();
+        p.name = "Jason Bourne";
+        p.birthDate = LocalDate.of(1999, 8, 7);
+        p.salary = new BigDecimal("123.45678");
+        String json = defaultJsonb.toJson(p);
+        assertEquals("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"123.46\"}", json);
+        
+        Person9 after = defaultJsonb.fromJson("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":\"123.46\"}", Person9.class);
+        assertEquals(p.name, after.name);
+        assertEquals(p.birthDate, after.birthDate);
+        assertEquals(new BigDecimal("123.46"), after.salary);
+    }
+    
+    public static class Person10 {
+        public String name;
+
+        public LocalDate birthDate;
+
+        public BigDecimal salary;
+    }
+    
+    @Test
+    public void testDateNumberFormats2() {
+        Person10 p = new Person10();
+        p.name = "Jason Bourne";
+        p.birthDate = LocalDate.of(1999, 8, 7);
+        p.salary = new BigDecimal("123.45678");
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()//
+                .withDateFormat("dd.MM.yyyy", null)); // TODO: why no withNumberFormat?
+        String json = jsonb.toJson(p);
+        assertEquals("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}", json);
+        
+        Person9 after = jsonb.fromJson("{\"birthDate\":\"07.08.1999\",\"name\":\"Jason Bourne\",\"salary\":123.45678}", Person9.class);
+        assertEquals(p.name, after.name);
+        assertEquals(p.birthDate, after.birthDate);
+        assertEquals(p.salary, after.salary);
+    }
+    
+    public static class Customer {
+        private int id;
+        private String name;
+        private String organization;
+        private String position;
+        
+        public int getId() {
+            return id;
+        }
+        public void setId(int id) {
+            this.id = id;
+        }
+        public String getName() {
+            return name;
+        }
+        public void setName(String name) {
+            this.name = name;
+        }
+        public String getOrganization() {
+            return organization;
+        }
+        public void setOrganization(String organization) {
+            this.organization = organization;
+        }
+        public String getPosition() {
+            return position;
+        }
+        public void setPosition(String position) {
+            this.position = position;
+        }
+    }
+
+    public static class CustomerAnnotated {
+        @JsonbProperty("customer_id")
+        private int id;
+
+        @JsonbProperty("customer_name")
+        private String name;
+        
+        public int getId() {
+            return id;
+        }
+        public void setId(int id) {
+            this.id = id;
+        }
+        public String getName() {
+            return name;
+        }
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    public static class CustomerAdapter implements JsonbAdapter<Customer, CustomerAnnotated> {
+        @Override
+        public CustomerAnnotated adaptToJson(Customer c) throws Exception {
+            CustomerAnnotated customer = new CustomerAnnotated();
+            customer.setId(c.getId());
+            customer.setName(c.getName());
+            return customer;
+        }
+
+        @Override
+        public Customer adaptFromJson(CustomerAnnotated adapted) throws Exception {
+            Customer customer = new Customer();
+            customer.setId(adapted.getId());
+            customer.setName(adapted.getName());
+            return customer;
+        }
+    }
+    
+    @Test
+    public void testAdapters1() {
+        // Create customer
+        Customer customer = new Customer();
+
+        customer.setId(1);
+        customer.setName("Jason Bourne");
+        customer.setOrganization("Super Agents");
+        customer.setPosition("Super Agent");
+
+        // Serialize
+        String json = defaultJsonb.toJson(customer);
+        assertEquals("{\"id\":1,\"name\":\"Jason Bourne\",\"organization\":\"Super Agents\",\"position\":\"Super Agent\"}", json);
+    }
+    
+    @Test
+    public void testAdapters2() {
+     // Create custom configuration
+        JsonbConfig config = new JsonbConfig()
+            .withAdapters(new CustomerAdapter());
+
+        // Create Jsonb with custom configuration
+        Jsonb jsonb = JsonbBuilder.create(config);
+
+        // Create customer
+        Customer customer = new Customer();
+
+        customer.setId(1);
+        customer.setName("Jason Bourne");
+        customer.setOrganization("Super Agents");
+        customer.setPosition("Super Agent");
+
+        // Serialize
+        String json = jsonb.toJson(customer);
+        assertEquals("{\"customer_id\":1,\"customer_name\":\"Jason Bourne\"}", json);
+    }
+    
+    public static class CustomerSerializer implements JsonbSerializer<Customer> {
+        @Override
+        public void serialize(Customer customer, JsonGenerator generator, SerializationContext ctx) {
+            generator.writeStartObject();
+            generator.write("customer_id", customer.getId());
+            generator.write("customer_name", customer.getName());
+            generator.writeEnd();
+        }
+    }
+
+    public static class CustomerDeserializer implements JsonbDeserializer<Customer> {
+        @Override
+        public Customer deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+            Customer customer = new Customer();
+            JsonParser.Event next;
+
+            // Moving parser by hand looking for customer_id and customer_name properties
+            while ((next = parser.next()) != JsonParser.Event.END_OBJECT) {
+                if (next == JsonParser.Event.KEY_NAME) {
+                    String jsonKeyName = parser.getString();
+
+                    // Move to json value
+                    parser.next();
+
+                    if ("customer_id".equals(jsonKeyName)) {
+                        customer.setId(parser.getInt());
+                    } else if ("customer_name".equals(jsonKeyName)) {
+                        customer.setName(parser.getString());
+                    }
+                }
+            }
+            return customer;
+        }
+    }
+    
+    @Test
+    public void testSerializerDeserializer() {
+        // Create pojo
+        Customer customer = new Customer();
+        customer.setId(1);
+        customer.setName("Freddie");
+        customer.setOrganization("Super Agents");
+        customer.setPosition("Super Agent");
+
+        // Also configurable with @JsonbSerializer / JsonbDeserializer on properties and class.
+        JsonbConfig config = new JsonbConfig()
+                .withSerializers(new CustomerSerializer())
+                .withDeserializers(new CustomerDeserializer());
+
+        Jsonb jsonb = JsonbBuilder.create(config);
+        String json = jsonb.toJson(customer);
+        assertEquals("{\"customer_id\":1,\"customer_name\":\"Freddie\"}", json);
+        
+        Customer result = jsonb.fromJson(json, Customer.class);
+        assertEquals(customer.getId(), result.getId());
+        assertEquals(customer.getName(), result.getName());
+        assertNull(result.getOrganization());
+        assertNull(result.getPosition());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/AnnotationFinderTest.java b/src/test/java/org/eclipse/yasson/internal/AnnotationFinderTest.java
new file mode 100644
index 0000000..0c0f509
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/AnnotationFinderTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import static org.eclipse.yasson.internal.AnnotationFinderTestFixtures.TESTVALUE;
+import static org.eclipse.yasson.internal.AnnotationFinderTestFixtures.getConstructorAnnotationsOf;
+import static org.eclipse.yasson.internal.AnnotationFinderTestFixtures.getMethodAnnotationsOf;
+
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.AnnotationAnnotatedWithDeprecated;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.AnnotationWithoutValueProperty;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithConstructAnnotation;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithDeprecatedAndIgnoredMethod;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithDeprecatedMethod;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithIgnoredAndInheritedDeprecatedMethod;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithIgnoredMethod;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithInheritedAndDirectlyDeprecatedMethod;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithInheritedDeprecatedMethod;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithMissingValuePropertyAnnotation;
+import org.eclipse.yasson.internal.AnnotationFinderTestFixtures.ObjectWithNoAnnotations;
+
+public class AnnotationFinderTest {
+
+    /**
+     * class under test.
+     */
+    private AnnotationFinder findAnnotation = AnnotationFinder.findAnnotation(Deprecated.class);
+
+    @Test
+    public void testAnnotationShouldBeFound() {
+        assertNotNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithDeprecatedMethod.class)));
+    }
+
+    @Test
+    public void testAnnotationShouldBeFoundWithinOthers() {
+        assertNotNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithDeprecatedAndIgnoredMethod.class)));
+    }
+
+    @Test
+    public void testInheritedAnnotationShouldBeFound() {
+        Deprecated annotation = findAnnotation.in(getMethodAnnotationsOf(ObjectWithInheritedDeprecatedMethod.class));
+        assertEquals("inherited", annotation.since());
+    }
+
+    @Test
+    public void testInheritedAnnotationShouldBeFoundWithinOthers() {
+        Deprecated annotation = findAnnotation.in(getMethodAnnotationsOf(ObjectWithIgnoredAndInheritedDeprecatedMethod.class));
+        assertEquals("inherited", annotation.since());
+    }
+
+    @Test
+    public void testDirectAnnotationShouldBePreferedOverInheritedOnes() {
+        Deprecated annotation = findAnnotation.in(getMethodAnnotationsOf(ObjectWithInheritedAndDirectlyDeprecatedMethod.class));
+        assertEquals("", annotation.since());
+    }
+
+    @Test
+    public void testResultShouldBeNullWhenThereIsNoAnnotation() {
+        assertNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithNoAnnotations.class)));
+    }
+
+    @Test
+    public void testResultShouldBeNullWhenThereAreOnlyOtherAnnotations() {
+        assertNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithIgnoredMethod.class)));
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testValueOfAnnotationShouldBeExtractedDynamically() {
+        findAnnotation = AnnotationFinder.findAnnotation(AnnotationAnnotatedWithDeprecated.class);
+        assertEquals(TESTVALUE, findAnnotation.valueIn(getMethodAnnotationsOf(ObjectWithInheritedDeprecatedMethod.class)));
+    }
+
+    @Test
+    public void testValueOfAnnotationShouldBeNullIfAnnotationDoesNotExist() {
+        findAnnotation = AnnotationFinder.findAnnotation(ObjectWithNoAnnotations.class);
+        assertNull(findAnnotation.valueIn(getMethodAnnotationsOf(ObjectWithInheritedDeprecatedMethod.class)));
+    }
+
+    @Test
+    public void testValueOfConstructorAnnotation() {
+        findAnnotation = AnnotationFinder.findConstructorProperties();
+        assertArrayEquals(new Object[] { TESTVALUE }, (Object[]) findAnnotation.valueIn(getConstructorAnnotationsOf(ObjectWithConstructAnnotation.class)));
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    public void testValueOfConstructorMetaAnnotation() {
+        findAnnotation = AnnotationFinder.findAnnotation(AnnotationAnnotatedWithDeprecated.class);
+        assertEquals(TESTVALUE, findAnnotation.valueIn(getConstructorAnnotationsOf(ObjectWithConstructAnnotation.class)));
+    }
+
+    @Test
+    public void testIgnorenNonExistingOptionalAnnotationClassName() {
+        findAnnotation = AnnotationFinder.findAnnotationByName("java.nnnooootttt.eexxxxiissttting.Class");
+        assertNull(findAnnotation.in(getMethodAnnotationsOf(ObjectWithIgnoredMethod.class)));
+    }
+
+    @Test
+    public void testIgnorenAnnotationAnnotationWithoutValueProperty() {
+        findAnnotation = AnnotationFinder.findAnnotation(AnnotationWithoutValueProperty.class);
+        assertNull(findAnnotation.valueIn(getMethodAnnotationsOf(ObjectWithMissingValuePropertyAnnotation.class)));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/AnnotationFinderTestFixtures.java b/src/test/java/org/eclipse/yasson/internal/AnnotationFinderTestFixtures.java
new file mode 100644
index 0000000..400d471
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/AnnotationFinderTestFixtures.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import org.junit.jupiter.api.*;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.beans.ConstructorProperties;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Disabled
+class AnnotationFinderTestFixtures {
+
+    public static final String TESTVALUE = "testvalue";
+
+    public static final Annotation[] getMethodAnnotationsOf(Class<?> clazz) {
+        try {
+            return clazz.getMethod("annotatedMethod").getAnnotations();
+        } catch (NoSuchMethodException | SecurityException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    public static final Annotation[] getConstructorAnnotationsOf(Class<?> clazz) {
+        try {
+            return clazz.getConstructor(String.class).getAnnotations();
+        } catch (NoSuchMethodException | SecurityException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    public static class ObjectWithNoAnnotations {
+        public void annotatedMethod() {
+            // empty
+        }
+    }
+
+    public static class ObjectWithDeprecatedMethod {
+        @Deprecated
+        public void annotatedMethod() {
+            // empty
+        }
+    }
+
+    public static class ObjectWithIgnoredMethod {
+        @Disabled
+        public void annotatedMethod() {
+            // empty
+        }
+    }
+
+    public static class ObjectWithDeprecatedAndIgnoredMethod {
+    	@Disabled
+        @Deprecated
+        public void annotatedMethod() {
+            // empty
+        }
+    }
+
+    public static class ObjectWithInheritedDeprecatedMethod {
+        @AnnotationAnnotatedWithDeprecated()
+        public void annotatedMethod() {
+            // empty
+        }
+    }
+
+    public static class ObjectWithIgnoredAndInheritedDeprecatedMethod {
+    	@Disabled
+        @AnnotationAnnotatedWithDeprecated
+        public void annotatedMethod() {
+            // empty
+        }
+    }
+
+    public static class ObjectWithInheritedAndDirectlyDeprecatedMethod {
+        @Deprecated
+        @AnnotationAnnotatedWithDeprecated
+        public void annotatedMethod() {
+            // empty
+        }
+    }
+
+    public static class ObjectWithConstructAnnotation {
+        @AnnotationAnnotatedWithDeprecated
+        @ConstructorProperties({ TESTVALUE })
+        public ObjectWithConstructAnnotation(String testvalue) {
+            // empty
+        }
+    }
+
+    public static class ObjectWithMissingValuePropertyAnnotation {
+        @AnnotationWithoutValueProperty
+        public void annotatedMethod() {
+            // empty
+        }
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(value = { METHOD, CONSTRUCTOR, TYPE })
+    @Deprecated(since = "inherited")
+    public @interface AnnotationAnnotatedWithDeprecated {
+
+        String value() default TESTVALUE;
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(value = { METHOD, CONSTRUCTOR, TYPE })
+    public @interface AnnotationWithoutValueProperty {
+
+        String someOtherProperty() default TESTVALUE;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTest.java b/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTest.java
new file mode 100644
index 0000000..3922730
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2019, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import jakarta.json.bind.config.PropertyNamingStrategy;
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertCreatedInstanceContainsAllParameters;
+import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertParameters;
+
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedConstructor;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedFactoryMethod;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedProtectedConstructor;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithTwoJsonbCreatorAnnotatedSpots;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithoutAnnotatedConstructor;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+
+import jakarta.json.spi.JsonProvider;
+
+/**
+ * Tests the {@link AnnotationIntrospector}.
+ * 
+ * @see AnnotationIntrospectorTestFixtures
+ * @see AnnotationIntrospectorTestAsserts
+ */
+public class AnnotationIntrospectorTest {
+    private final JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider());
+    private final PropertyNamingStrategy propertyNamingStrategy = jsonbContext.getConfigProperties().getPropertyNamingStrategy();
+
+    /**
+     * class under test.
+     */
+    private AnnotationIntrospector instrospector = new AnnotationIntrospector(jsonbContext);
+
+    @Test
+    public void testObjectShouldBeCreateableFromJsonbAnnotatedConstructor() {
+        JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedConstructor.class, propertyNamingStrategy);
+        assertParameters(ObjectWithJsonbCreatorAnnotatedConstructor.parameters(), creator);
+        assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedConstructor.example(), creator);
+    }
+
+    @Test
+    public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethod() {
+        JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedFactoryMethod.class, propertyNamingStrategy);
+        assertParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.parameters(), creator);
+        assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.example(), creator);
+    }
+
+    @Test
+    public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethodIgnoringConstructorPorperties() {
+        JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.class, propertyNamingStrategy);
+        assertParameters(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.parameters(), creator);
+        assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation.example(), creator);
+    }
+
+    @Test
+    public void testJsonbAnnotatedProtectedConstructorLeadsToAnException() {
+        assertThrows(JsonbException.class, () -> {
+	        JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedProtectedConstructor.class, propertyNamingStrategy);
+	        assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedProtectedConstructor.example(), creator);
+        });
+    }
+
+    // TODO Under discussion: https://github.com/eclipse-ee4j/yasson/issues/326
+    @Disabled
+    @Test
+    public void testNoArgConstructorShouldBePreferredOverUnusableJsonbAnnotatedProtectedConstructor() {
+        JsonbCreator creator = instrospector.getCreator(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.class, propertyNamingStrategy);
+        assertParameters(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.parameters(), creator);
+        assertCreatedInstanceContainsAllParameters(ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor.example(), creator);
+    }
+
+    @Test
+    public void testMoreThanOneAnnotatedCreatorMethodShouldLeadToAnException() {
+        assertThrows(JsonbException.class, 
+        			() -> instrospector.getCreator(ObjectWithTwoJsonbCreatorAnnotatedSpots.class, propertyNamingStrategy),
+        			() -> "More than one @" + JsonbCreator.class.getSimpleName());
+    }
+
+    @Test
+    public void testCreatorShouldBeNullOnMissingConstructorAnnotation() {
+        assertNull(instrospector.getCreator(ObjectWithoutAnnotatedConstructor.class, propertyNamingStrategy));
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTestAsserts.java b/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTestAsserts.java
new file mode 100644
index 0000000..fe64088
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTestAsserts.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ProvidesParameterRepresentation;
+import org.eclipse.yasson.internal.model.CreatorModel;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+import java.util.Map.Entry;
+
+@Disabled
+class AnnotationIntrospectorTestAsserts {
+
+    /**
+     * Creates a new Instance of the Object using the given {@link JsonbCreator} and compares the result with the given
+     * expected Object. The comparison is done by comparing both {@link ProvidesParameterRepresentation#asParameters()}.
+     * 
+     * @param expected {@link ProvidesParameterRepresentation}
+     * @param creator  {@link JsonbCreator}
+     */
+    public static <T extends ProvidesParameterRepresentation> void assertCreatedInstanceContainsAllParameters(T expected, JsonbCreator creator) {
+        assertNotNull(creator, JsonbCreator.class.getSimpleName() + " expected");
+        @SuppressWarnings("unchecked")
+        T created = (T) creator.call(expected.asParameters(), expected.getClass());
+        assertArrayEquals(expected.asParameters(), created.asParameters());
+    }
+
+    /**
+     * Compares the expected parameters with the meta-model contained in the {@link JsonbCreator}.
+     * 
+     * @param expectedParameters {@link Map} with {@link String}-Key and {@link Type}-Value
+     * @param creator            CreatorModel
+     */
+    public static void assertParameters(Map<String, Type> expectedParameters, JsonbCreator creator) {
+        assertNotNull(creator, JsonbCreator.class.getSimpleName() + " expected");
+        for (Entry<String, Type> parameter : expectedParameters.entrySet()) {
+            CreatorModel creatorModel = creator.findByName(parameter.getKey());
+            assertEquals(parameter.getKey(), creatorModel.getName());
+            assertEquals(parameter.getValue(), creatorModel.getType());
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTestFixtures.java b/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTestFixtures.java
new file mode 100644
index 0000000..6b6b696
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorTestFixtures.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import org.junit.jupiter.api.*;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+import java.beans.ConstructorProperties;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+
+@Disabled
+class AnnotationIntrospectorTestFixtures {
+
+    public static interface ProvidesParameterRepresentation {
+        Object[] asParameters();
+    }
+
+    private static final Map<String, Type> twoParameters(String name1, Type type1, String name2, Type type2) {
+        Map<String, Type> parameters = new HashMap<>();
+        parameters.put(name1, type1);
+        parameters.put(name2, type2);
+        return parameters;
+    }
+
+    public static Constructor<?>[] constructorsOf(Class<?> clazz) {
+        return AccessController.doPrivileged((PrivilegedAction<Constructor<?>[]>) clazz::getDeclaredConstructors);
+    }
+
+    public static class ObjectWithoutAnnotatedConstructor implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithoutAnnotatedConstructor("a string", Long.MAX_VALUE);
+        }
+
+        public ObjectWithoutAnnotatedConstructor(String aString, long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithNotAnnotatedConstructor [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithJsonbCreatorAnnotatedConstructor implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithJsonbCreatorAnnotatedConstructor("a string", Long.MAX_VALUE);
+        }
+
+        @JsonbCreator
+        public ObjectWithJsonbCreatorAnnotatedConstructor( //
+                @JsonbProperty("string") String aString, //
+                @JsonbProperty("primitive") long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithJsonbCreatorAnnotatedConstructor [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithJsonbCreatorAnnotatedProtectedConstructor implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithJsonbCreatorAnnotatedProtectedConstructor("a string", Long.MAX_VALUE);
+        }
+
+        @JsonbCreator
+        protected ObjectWithJsonbCreatorAnnotatedProtectedConstructor( //
+                @JsonbProperty("string") String aString, //
+                @JsonbProperty("primitive") long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithJsonbCreatorAnnotatedProtectedConstructor [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor implements ProvidesParameterRepresentation {
+        private String string;
+        private long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor("a string", Long.MAX_VALUE);
+        }
+
+        public ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor() {
+            super();
+        }
+
+        @JsonbCreator
+        protected ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor( //
+                @JsonbProperty("string") String aString, //
+                @JsonbProperty("primitive") long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        public String getString() {
+            return string;
+        }
+
+        public void setString(String string) {
+            this.string = string;
+        }
+
+        public long getPrimitive() {
+            return primitive;
+        }
+
+        public void setPrimitive(long primitive) {
+            this.primitive = primitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithNoArgAndJsonbCreatorAnnotatedProtectedConstructor [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithJsonbCreatorAnnotatedFactoryMethod implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithJsonbCreatorAnnotatedFactoryMethod("text", Long.MIN_VALUE);
+        }
+
+        @JsonbCreator
+        public static final ObjectWithJsonbCreatorAnnotatedFactoryMethod create( //
+                @JsonbProperty("string") String aString, //
+                @JsonbProperty("primitive") long aPrimitive) {
+            return new ObjectWithJsonbCreatorAnnotatedFactoryMethod(aString, aPrimitive);
+        }
+
+        private ObjectWithJsonbCreatorAnnotatedFactoryMethod(String string, long primitiv) {
+            this.string = string;
+            this.primitive = primitiv;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithJsonbCreatorAnnotatedFactoryMethod [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithTwoJsonbCreatorAnnotatedSpots implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithJsonbCreatorAnnotatedConstructor("", Long.valueOf(0));
+        }
+
+        @JsonbCreator
+        public static final ObjectWithTwoJsonbCreatorAnnotatedSpots create( //
+                @JsonbProperty("string") String aString, //
+                @JsonbProperty("primitive") long aPrimitive) {
+            return new ObjectWithTwoJsonbCreatorAnnotatedSpots(aString, aPrimitive);
+        }
+
+        @JsonbCreator
+        public ObjectWithTwoJsonbCreatorAnnotatedSpots( //
+                @JsonbProperty("string") String aString, //
+                @JsonbProperty("primitive") long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithTwoJsonbCreatorAnnotatedSpots [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithConstructorPropertiesAnnotation implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithConstructorPropertiesAnnotation("  ", Long.valueOf(-12));
+        }
+
+        @ConstructorProperties({ "string", "primitive" })
+        public ObjectWithConstructorPropertiesAnnotation(String aString, long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithConstructorPropertiesAnnotatedConstructor [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithTwoConstructorPropertiesAnnotation implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithTwoConstructorPropertiesAnnotation("  ", Long.valueOf(-12));
+        }
+
+        @ConstructorProperties({ "string" })
+        public ObjectWithTwoConstructorPropertiesAnnotation(String aString) {
+            this(aString, 0L);
+        }
+
+        @ConstructorProperties({ "string", "primitive" })
+        public ObjectWithTwoConstructorPropertiesAnnotation(String aString, long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithTwoConstructorPropertiesAnnotation [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation("", Long.valueOf(0));
+        }
+
+        @JsonbCreator
+        public static final ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation create( //
+                @JsonbProperty("string") String aString, //
+                @JsonbProperty("primitive") long aPrimitive) {
+            return new ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation(aString, aPrimitive);
+        }
+
+        @ConstructorProperties({ "string", "primitive" })
+        public ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation(String aString, long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithPublicNoArgAndAnnotatedPrivateConstructor implements ProvidesParameterRepresentation {
+        private String string;
+        private Long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithPublicNoArgAndAnnotatedPrivateConstructor("  ", Long.valueOf(-12));
+        }
+
+        public ObjectWithPublicNoArgAndAnnotatedPrivateConstructor() {
+            super();
+        }
+
+        @ConstructorProperties({ "string", "primitive" })
+        private ObjectWithPublicNoArgAndAnnotatedPrivateConstructor(String aString, long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        public Long getPrimitive() {
+            return primitive;
+        }
+
+        public void setPrimitive(Long primitive) {
+            this.primitive = primitive;
+        }
+
+        public String getString() {
+            return string;
+        }
+
+        public void setString(String string) {
+            this.string = string;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithPublicNoArgAndAnnotatedPrivateConstructor [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor implements ProvidesParameterRepresentation {
+        private String string;
+        private Long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor("  ", Long.valueOf(-12));
+        }
+
+        public static final ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor create(String aString, long aPrimitive) {
+            return new ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor(aString, aPrimitive);
+        }
+
+        public ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor() {
+            super();
+        }
+
+        @ConstructorProperties({ "string", "primitive" })
+        ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor(String aString, long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        public Long getPrimitive() {
+            return primitive;
+        }
+
+        public void setPrimitive(Long primitive) {
+            this.primitive = primitive;
+        }
+
+        public String getString() {
+            return string;
+        }
+
+        public void setString(String string) {
+            this.string = string;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithPublicNoArgAndAnnotatedProtectedConstructor implements ProvidesParameterRepresentation {
+        private String string;
+        private Long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor("  ", Long.valueOf(-12));
+        }
+
+        public static final ObjectWithPublicNoArgAndAnnotatedProtectedConstructor create(String aString, long aPrimitive) {
+            return new ObjectWithPublicNoArgAndAnnotatedProtectedConstructor(aString, aPrimitive);
+        }
+
+        @ConstructorProperties({ "string", "primitive" })
+        protected ObjectWithPublicNoArgAndAnnotatedProtectedConstructor(String aString, long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        public ObjectWithPublicNoArgAndAnnotatedProtectedConstructor() {
+            super();
+        }
+
+        public Long getPrimitive() {
+            return primitive;
+        }
+
+        public void setPrimitive(Long primitive) {
+            this.primitive = primitive;
+        }
+
+        public String getString() {
+            return string;
+        }
+
+        public void setString(String string) {
+            this.string = string;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithJsonbCreatorAndConstructorPropertiesAnnotation [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+
+    public static class ObjectWithMissingConstructorAnnotation implements ProvidesParameterRepresentation {
+        private final String string;
+        private final long primitive;
+
+        public static final Map<String, Type> parameters() {
+            return twoParameters("string", String.class, "primitive", long.class);
+        }
+
+        public static final ProvidesParameterRepresentation example() {
+            return new ObjectWithMissingConstructorAnnotation("a string", Long.MAX_VALUE);
+        }
+
+        public ObjectWithMissingConstructorAnnotation(String aString, long aPrimitive) {
+            this.string = aString;
+            this.primitive = aPrimitive;
+        }
+
+        @Override
+        public Object[] asParameters() {
+            return new Object[] { string, primitive };
+        }
+
+        @Override
+        public String toString() {
+            return "ObjectWithMissingConstructorAnnotation [string=" + string + ", primitive=" + primitive + "]";
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorWithoutOptionalModulesTest.java b/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorWithoutOptionalModulesTest.java
new file mode 100644
index 0000000..855c5f9
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/AnnotationIntrospectorWithoutOptionalModulesTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertCreatedInstanceContainsAllParameters;
+import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertParameters;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+import jakarta.json.spi.JsonProvider;
+
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedConstructor;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedFactoryMethod;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithoutAnnotatedConstructor;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests the {@link AnnotationIntrospector} with missing optional module "java.deskop", <br>
+ * that contains the ConstructorProperties-Annotation.
+ * <p>
+ * Requires --limit-modules java.base,java.logging,java.sql (to exclude java.desktop) to work. <br>
+ * See pom.xml surefire plugin configuration.
+ * 
+ * @see AnnotationIntrospectorTestFixtures
+ */
+public class AnnotationIntrospectorWithoutOptionalModulesTest {
+
+    /**
+     * class under test.
+     */
+    private static final AnnotationIntrospector instrospector = new AnnotationIntrospector(new JsonbContext(new JsonbConfig(), JsonProvider.provider()));
+    private final PropertyNamingStrategy propertyNamingStrategy = propertyName -> propertyName;
+
+    @Test
+    public void testNoConstructorPropertiesAnnotationWithoutOptionalModules() {
+        String className = "java.beans.ConstructorProperties";
+        try {
+            Class.forName(className);
+            fail("Class [" + className + "] should not be available for this test.");
+        } catch (ClassNotFoundException e) {
+            // OK, as expected
+        }
+    }
+
+    @Test
+    public void testCreatorShouldBeNullOnMissingConstructorAnnotation() {
+        assertNull(instrospector.getCreator(ObjectWithoutAnnotatedConstructor.class, propertyNamingStrategy));
+    }
+
+    @Test
+    public void testObjectShouldBeCreateableFromJsonbAnnotatedConstructorWithoutOptionalModules() {
+        JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedConstructor.class, propertyNamingStrategy);
+        assertParameters(ObjectWithJsonbCreatorAnnotatedConstructor.parameters(), creator);
+        assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedConstructor.example(), creator);
+    }
+
+    @Test
+    public void testObjectShouldBeCreateableFromJsonbAnnotatedStaticFactoryMethodWithoutOptionalModules() {
+        JsonbCreator creator = instrospector.getCreator(ObjectWithJsonbCreatorAnnotatedFactoryMethod.class, propertyNamingStrategy);
+        assertParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.parameters(), creator);
+        assertCreatedInstanceContainsAllParameters(ObjectWithJsonbCreatorAnnotatedFactoryMethod.example(), creator);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/ClassParserTest.java b/src/test/java/org/eclipse/yasson/internal/ClassParserTest.java
new file mode 100644
index 0000000..520645a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/ClassParserTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import org.eclipse.yasson.internal.model.customization.ClassCustomization;
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.defaultmapping.modifiers.model.FieldModifiersClass;
+import org.eclipse.yasson.defaultmapping.modifiers.model.MethodModifiersClass;
+import org.eclipse.yasson.internal.model.ClassModel;
+import org.eclipse.yasson.internal.model.JsonbAnnotatedElement;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.spi.JsonProvider;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+/**
+ * Test for ClassParser component.
+ *
+ * @author Roman Grigoriadi
+ */
+public class ClassParserTest {
+    private static final JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider());
+    private static final ClassParser classParser = new ClassParser(jsonbContext);
+    private static final AnnotationIntrospector introspector = new AnnotationIntrospector(jsonbContext);
+
+    @Test
+    public void testDefaultMappingFieldModifiers() {
+        final JsonbAnnotatedElement<Class<?>> clsElement = introspector.collectAnnotations(FieldModifiersClass.class);
+        ClassModel model = new ClassModel(FieldModifiersClass.class, introspector.introspectCustomization(clsElement,
+                                                                                                          ClassCustomization.empty(), jsonbContext.getConfigProperties().getPropertyNamingStrategy()), null, null);
+        classParser.parseProperties(model, clsElement);
+        assertTrue(model.getPropertyModel("finalString").isReadable());
+        assertFalse(model.getPropertyModel("finalString").isWritable());
+        assertFalse(model.getPropertyModel("staticString").isReadable());
+        assertFalse(model.getPropertyModel("staticString").isWritable());
+        assertFalse(model.getPropertyModel("transientString").isReadable());
+        assertFalse(model.getPropertyModel("transientString").isWritable());
+    }
+
+    @Test
+    public void testDefaultMappingMethodModifiers() {
+        final JsonbAnnotatedElement<Class<?>> clsElement = introspector.collectAnnotations(MethodModifiersClass.class);
+        ClassModel model = new ClassModel(FieldModifiersClass.class, introspector.introspectCustomization(clsElement,
+                                                                                                          ClassCustomization.empty(), jsonbContext.getConfigProperties().getPropertyNamingStrategy()), null, null);
+        classParser.parseProperties(model, clsElement);
+        assertFalse(model.getPropertyModel("publicFieldWithPrivateMethods").isReadable());
+        assertFalse(model.getPropertyModel("publicFieldWithPrivateMethods").isWritable());
+        assertTrue(model.getPropertyModel("publicFieldWithoutMethods").isReadable());
+        assertTrue(model.getPropertyModel("publicFieldWithoutMethods").isWritable());
+        assertTrue(model.getPropertyModel("getterWithoutFieldValue").isReadable());
+        assertTrue(model.getPropertyModel("getterWithoutFieldValue").isWritable());
+
+
+        MethodModifiersClass object = new MethodModifiersClass();
+        final AtomicReference<String> accepted = new AtomicReference<>();
+        Consumer<String> withoutFieldConsumer = accepted::set;
+        object.setSetterWithoutFieldConsumer(withoutFieldConsumer);
+        model.getPropertyModel("getterWithoutFieldValue").setValue(object, "ACCEPTED_VALUE");
+        assertEquals("ACCEPTED_VALUE", accepted.get());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/CollectionsWithJavaBaseTypesTest.java b/src/test/java/org/eclipse/yasson/internal/CollectionsWithJavaBaseTypesTest.java
new file mode 100644
index 0000000..838a6f3
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/CollectionsWithJavaBaseTypesTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.time.LocalDate;
+import java.util.*;
+
+/**
+ * Contains java.base types inside collections.
+ * Tests that yasson won't try to parse this types into ClassModel.
+ */
+public class CollectionsWithJavaBaseTypesTest {
+
+    @Test
+    public void testSimple() {
+        JavaBasePropertiesInContainer properties = new JavaBasePropertiesInContainer();
+        properties.setNames(Arrays.asList("First", "second", null));
+        
+        for(int i = 0; i < 3; i++) {
+            List<Integer> integerList = new ArrayList<>();
+            integerList.add(0);
+            integerList.add(1);
+            integerList.add(null);
+            properties.setListOfListsOfIntegers(new ArrayList<>());
+            properties.getListOfListsOfIntegers().add(integerList);
+        }
+        properties.setOptionalInts(new ArrayList<>());
+        properties.getOptionalInts().add(Optional.of(Integer.MAX_VALUE));
+        properties.getOptionalInts().add(Optional.empty());
+        properties.setDates(new ArrayList<>());
+        properties.getDates().add(LocalDate.of(2020, 1, 1));
+        properties.getDates().add(null);
+
+        Integer[][] innerArrayInts = new Integer[1][2];
+        innerArrayInts[0][0] = 1;
+        innerArrayInts[0][1] = null;
+        properties.setInnerArrayInts(innerArrayInts);
+
+        //Making all fields accessible. When used on JPMS will fail if java.base classes are parsed into class model.
+        Jsonb jsonb =
+                new JsonBindingBuilder().withConfig(new JsonbConfig().withNullValues(Boolean.TRUE)
+                        .withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() {
+            @Override
+            public boolean isVisible(Field field) {
+                return true;
+            }
+
+            @Override
+            public boolean isVisible(Method method) {
+                return true;
+            }
+        })).build();
+
+        String expected = "{\"dates\":[\"2020-01-01\",null],\"innerArrayInts\":[[1,null]],\"listOfListsOfIntegers\":[[0,1,null]],\"names\":[\"First\",\"second\",null],\"optionalInts\":[2147483647,null]}";
+        assertEquals(expected, jsonb.toJson(properties));
+
+        JavaBasePropertiesInContainer result = jsonb.fromJson(expected, JavaBasePropertiesInContainer.class);
+        assertEquals(properties.getNames(), result.getNames());
+        assertEquals(properties.getOptionalInts(), result.getOptionalInts());
+        assertEquals(properties.getListOfListsOfIntegers(), result.getListOfListsOfIntegers());
+        assertArrayEquals(properties.getInnerArrayInts(), result.getInnerArrayInts());
+    }
+
+    public static final class JavaBasePropertiesInContainer {
+
+        private List<String> names;
+
+        private List<Optional<Integer>> optionalInts;
+
+        private List<LocalDate> dates;
+
+        public Integer[][] getInnerArrayInts() {
+            return innerArrayInts;
+        }
+
+        public void setInnerArrayInts(Integer[][] innerArrayInts) {
+            this.innerArrayInts = innerArrayInts;
+        }
+
+        private Integer[][] innerArrayInts;
+
+        public List<LocalDate> getDates() {
+            return dates;
+        }
+
+        public void setDates(List<LocalDate> dates) {
+            this.dates = dates;
+        }
+
+        public List<Optional<Integer>> getOptionalInts() {
+            return optionalInts;
+        }
+
+        public void setOptionalInts(List<Optional<Integer>> optionalInts) {
+            this.optionalInts = optionalInts;
+        }
+
+        List<List<Integer>> listOfListsOfIntegers;
+
+        public List<List<Integer>> getListOfListsOfIntegers() {
+            return listOfListsOfIntegers;
+        }
+
+        public void setListOfListsOfIntegers(List<List<Integer>> listOfListsOfIntegers) {
+            this.listOfListsOfIntegers = listOfListsOfIntegers;
+        }
+
+        public List<String> getNames() {
+            return names;
+        }
+
+        public void setNames(List<String> names) {
+            this.names = names;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospectorTest.java b/src/test/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospectorTest.java
new file mode 100644
index 0000000..4639060
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/ConstructorPropertiesAnnotationIntrospectorTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertCreatedInstanceContainsAllParameters;
+import static org.eclipse.yasson.internal.AnnotationIntrospectorTestAsserts.assertParameters;
+import static org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.constructorsOf;
+
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithConstructorPropertiesAnnotation;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithJsonbCreatorAnnotatedConstructor;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithPublicNoArgAndAnnotatedPrivateConstructor;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithPublicNoArgAndAnnotatedProtectedConstructor;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithTwoConstructorPropertiesAnnotation;
+import org.eclipse.yasson.internal.AnnotationIntrospectorTestFixtures.ObjectWithoutAnnotatedConstructor;
+import org.eclipse.yasson.internal.model.JsonbCreator;
+
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.spi.JsonProvider;
+
+public class ConstructorPropertiesAnnotationIntrospectorTest {
+
+    private final JsonbContext jsonbContext = new JsonbContext(new JsonbConfig(), JsonProvider.provider());
+    private final AnnotationFinder constructorPropertiesFinder = AnnotationFinder.findConstructorProperties();
+
+    /**
+     * class under test.
+     */
+    private ConstructorPropertiesAnnotationIntrospector instrospector = new ConstructorPropertiesAnnotationIntrospector(jsonbContext, constructorPropertiesFinder);
+
+    @Test
+    public void testObjectShouldBeCreateableFromConstructorPropertiesAnnotatedConstructor() {
+        JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithConstructorPropertiesAnnotation.class));
+        assertParameters(ObjectWithConstructorPropertiesAnnotation.parameters(), creator);
+        assertCreatedInstanceContainsAllParameters(ObjectWithConstructorPropertiesAnnotation.example(), creator);
+    }
+
+    @Test
+    public void testShouldAlsoWorkWithStaticFactoryMethodAndPredefinedAnnotationFinder() {
+        instrospector = ConstructorPropertiesAnnotationIntrospector.forContext(jsonbContext);
+        JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithConstructorPropertiesAnnotation.class));
+        assertNotNull(creator);
+    }
+
+    @Test
+    public void testNullShouldBeReturnedWhenThereIsNoCreatorAnnotation() {
+        JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithoutAnnotatedConstructor.class));
+        assertNull(creator);
+    }
+
+    @Test
+    public void testNullShouldBeReturnedWhenThereIsNoConstructorPropertiesAnnotation() {
+        JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithJsonbCreatorAnnotatedConstructor.class));
+        assertNull(creator);
+    }
+
+    @Test
+    public void testNullShouldBeReturnedWhenThereAreMoreThanOneConstructorPropertiesAnnotation() {
+        JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithTwoConstructorPropertiesAnnotation.class));
+        assertNull(creator);
+    }
+
+    @Test
+    public void testAnnotatedInaccessiblePrivateConstructorShouldBeIgnored() {
+        JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithPublicNoArgAndAnnotatedPrivateConstructor.class));
+        assertNull(creator);
+    }
+
+    @Test
+    public void testAnnotatedInaccessiblePackageProtectedConstructorShouldBeIgnored() {
+        JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithPublicNoArgAndAnnotatedPackageProtectedConstructor.class));
+        assertNull(creator);
+    }
+
+    @Test
+    public void testAnnotatedInaccessibleProtectedConstructorShouldBeIgnored() {
+        JsonbCreator creator = instrospector.getCreator(constructorsOf(ObjectWithPublicNoArgAndAnnotatedProtectedConstructor.class));
+        assertNull(creator);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/JsonBindingTest.java b/src/test/java/org/eclipse/yasson/internal/JsonBindingTest.java
new file mode 100644
index 0000000..dc6faf2
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/JsonBindingTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019, 2020 IBM 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.yasson.internal;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.lang.reflect.Field;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+
+import org.eclipse.yasson.YassonConfig;
+import org.eclipse.yasson.internal.model.ClassModel;
+import org.junit.jupiter.api.Test;
+
+public class JsonBindingTest {
+    
+    public static class EagerParseClass {
+        public String foo;
+    }
+    
+    @Test
+    public void testEagerInit() throws Exception {
+        Jsonb jsonb = JsonbBuilder.create(new YassonConfig()
+                .withEagerParsing(EagerParseClass.class));
+        assertNotNull(getClassModel(jsonb, EagerParseClass.class));
+        
+        EagerParseClass obj = new EagerParseClass();
+        obj.foo = "foo";
+        assertEquals("{\"foo\":\"foo\"}", jsonb.toJson(obj));
+    }
+    
+    @Test
+    public void testNoEagerInit() throws Exception {
+        Jsonb jsonb = JsonbBuilder.create();
+        assertNull(getClassModel(jsonb, EagerParseClass.class));
+        
+        EagerParseClass obj = new EagerParseClass();
+        obj.foo = "foo";
+        assertEquals("{\"foo\":\"foo\"}", jsonb.toJson(obj));
+        
+        assertNotNull(getClassModel(jsonb, EagerParseClass.class));
+    }
+    
+    private ClassModel getClassModel(Jsonb jsonb, Class<?> clazz) throws Exception {
+        // Do some hacks to ensure that the class had a ClassModel registered
+        JsonBinding yasson = (JsonBinding) jsonb;
+        Field jsonbContext = yasson.getClass().getDeclaredField("jsonbContext");
+        jsonbContext.setAccessible(true);
+        JsonbContext ctx = (JsonbContext) jsonbContext.get(yasson);
+        return ctx.getMappingContext().getClassModel(clazz);
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/ReflectionUtilsTest.java b/src/test/java/org/eclipse/yasson/internal/ReflectionUtilsTest.java
new file mode 100644
index 0000000..add608c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/ReflectionUtilsTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class ReflectionUtilsTest {
+
+    public static class Types<T> {
+
+        public List<String> resolvedParameterizedField;
+
+        public List<Map<Integer, String>> resolvedNestedParameterizedField;
+
+        public String resolvedStr;
+
+        public List<T> unresolvedParameterizedField;
+
+        public List<Map<Integer, T>> unresolvedNestedParameterizedField;
+
+        public T unresolvedField;
+
+        public List<?> unresolvedWildcardField;
+    }
+
+
+    @Test
+    public void testIsTypeResolved() {
+        Types<String> types = new Types<>();
+        assertTrue(ReflectionUtils.isResolvedType(getFieldType("resolvedParameterizedField")));
+        assertTrue(ReflectionUtils.isResolvedType(getFieldType("resolvedNestedParameterizedField")));
+        assertTrue(ReflectionUtils.isResolvedType(getFieldType("resolvedStr")));
+        assertFalse(ReflectionUtils.isResolvedType(getFieldType("unresolvedParameterizedField")));
+        assertFalse(ReflectionUtils.isResolvedType(getFieldType("unresolvedNestedParameterizedField")));
+        assertFalse(ReflectionUtils.isResolvedType(getFieldType("unresolvedField")));
+        assertFalse(ReflectionUtils.isResolvedType(getFieldType("unresolvedWildcardField")));
+    }
+
+    private static Type getFieldType(String fieldName) {
+        try {
+            Field field = Types.class.getField(fieldName);
+            return field.getGenericType();
+        } catch (NoSuchFieldException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/AdaptedPojo.java b/src/test/java/org/eclipse/yasson/internal/cdi/AdaptedPojo.java
new file mode 100644
index 0000000..ad1b6c6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/AdaptedPojo.java
@@ -0,0 +1,33 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.json.bind.annotation.JsonbTypeAdapter;
+
+/**
+ * @author Roman Grigoriadi
+ */
+
+public class AdaptedPojo {
+
+    @JsonbTypeAdapter(CdiDependentAdapter.class)
+    public String adaptedValue1 = "1111";
+
+    @JsonbTypeAdapter(CdiDependentAdapter.class)
+    public String adaptedValue2 = "1001";
+
+    @JsonbTypeAdapter(NonCdiAdapter.class)
+    public String adaptedValue3 = "1010";
+
+
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/CalledMethods.java b/src/test/java/org/eclipse/yasson/internal/cdi/CalledMethods.java
new file mode 100644
index 0000000..ce7b402
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/CalledMethods.java
@@ -0,0 +1,40 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Mockito like method call counter for CDI beans.
+ *
+ * @author Roman Grigoriadi
+ */
+@ApplicationScoped
+public class CalledMethods {
+
+    /**
+     * Maps method name to called count.
+     */
+    private Map<String, Integer> results = new HashMap<>();
+
+    public void registerCall(@Observes MethodCalledEvent methodCalledEvent) {
+        results.compute(methodCalledEvent.getMethodName(), (s, c) -> c == null ? 1 : c + 1);
+    }
+
+    public Map<String, Integer> getResults() {
+        return results;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/CdiDependentAdapter.java b/src/test/java/org/eclipse/yasson/internal/cdi/CdiDependentAdapter.java
new file mode 100644
index 0000000..76eb268
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/CdiDependentAdapter.java
@@ -0,0 +1,53 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+/**
+ * Test scope components for testing CDI injection and cleanup.
+ *
+ * @author Roman Grigoriadi
+ */
+@ApplicationScoped
+public class CdiDependentAdapter implements JsonbAdapter<String, Integer> {
+
+    @Inject
+    private CdiTestService cdiTestService;
+
+    @Inject
+    @Hello2
+    private IHelloService helloService;
+
+    @Override
+    public Integer adaptToJson(String obj) throws Exception {
+        cdiTestService.runService();
+        helloService.sayHello();
+        return Integer.valueOf(obj);
+    }
+
+    @Override
+    public String adaptFromJson(Integer obj) throws Exception {
+        return String.valueOf(obj);
+    }
+
+    public CdiTestService getCdiTestService() {
+        return cdiTestService;
+    }
+
+    public IHelloService getHelloService() {
+        return helloService;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/CdiInjectionTest.java b/src/test/java/org/eclipse/yasson/internal/cdi/CdiInjectionTest.java
new file mode 100644
index 0000000..b17770c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/CdiInjectionTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory;
+
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.enterprise.inject.spi.CDI;
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CdiInjectionTest {
+
+    @Test
+    public void testInjectionAndCleanup() throws Exception {
+        WeldManager weldManager = new WeldManager();
+        weldManager.startWeld(CalledMethods.class, CdiTestService.class, HelloService1.class, HelloService2.class);
+
+        Jsonb jsonb = JsonbBuilder.create();
+        final String result = jsonb.toJson(new AdaptedPojo());
+        jsonb.close();
+        assertEquals("{\"adaptedValue1\":1111,\"adaptedValue2\":1001,\"adaptedValue3\":1010}", result);
+
+        //HelloService1 is @ApplicationScoped
+        CalledMethods calledMethods = getCalledMethods();
+        Map<String, Integer> results = calledMethods.getResults();
+
+        assertTrue(results.containsKey(HelloService1.class.getName() + ".sayHello"));
+        assertFalse(results.containsKey(HelloService1.class.getName() + ".preDestroy"));
+
+        //HelloService2 is @Dependent
+        assertTrue(results.containsKey(HelloService2.class.getName() + ".sayHello"));
+        assertTrue(results.containsKey(HelloService2.class.getName() + ".preDestroy"));
+
+        //CdiTestService is @ApplicationScoped
+        assertTrue(results.containsKey(CdiTestService.class.getName() + ".runService"));
+        assertFalse(results.containsKey(CdiTestService.class.getName() + ".preDestroy"));
+//        getCalledMethods().printCalled();
+
+
+        weldManager.shutdownWeld();
+
+        assertTrue(results.containsKey(CdiTestService.class.getName() + ".preDestroy"));
+        assertTrue(results.containsKey(HelloService1.class.getName() + ".preDestroy"));
+
+//        getCalledMethods().printCalled();
+    }
+
+    @Test
+    public void testInJndiEnvironment() throws NamingException {
+        InitialContext context = new InitialContext();
+        context.bind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME, new JndiBeanManager());
+
+        String result;
+        try {
+            Jsonb jsonb = JsonbBuilder.create();
+            result = jsonb.toJson(new AdaptedPojo());
+        } finally {
+            context.unbind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME);
+        }
+        assertEquals("{\"adaptedValue1\":1111,\"adaptedValue2\":1001,\"adaptedValue3\":1010}", result);
+    }
+
+    @Test
+    public void testNonCdiEnvironment() {
+        JsonbConfig config = new JsonbConfig();
+        //allow only field with components that doesn't has cdi dependencies.
+        config.withPropertyVisibilityStrategy(new PropertyVisibilityStrategy() {
+            @Override
+            public boolean isVisible(Field field) {
+                return "adaptedValue3".equals(field.getName());
+            }
+
+            @Override
+            public boolean isVisible(Method method) {
+                return false;
+            }
+        });
+        Jsonb jsonb = JsonbBuilder.create(config);
+        final String result = jsonb.toJson(new AdaptedPojo());
+        assertEquals("{\"adaptedValue3\":1010}", result);
+    }
+
+    private CalledMethods getCalledMethods() {
+        final BeanManager beanManager = CDI.current().getBeanManager();
+        final Bean<?> resolve = beanManager.resolve(beanManager.getBeans(CalledMethods.class));
+        return (CalledMethods) beanManager.getReference(resolve, CalledMethods.class, beanManager.createCreationalContext(resolve));
+    }
+
+
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/CdiTestService.java b/src/test/java/org/eclipse/yasson/internal/cdi/CdiTestService.java
new file mode 100644
index 0000000..9a68f4e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/CdiTestService.java
@@ -0,0 +1,50 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Event;
+import jakarta.inject.Inject;
+
+/**
+ * CDI test service
+ * @author Roman Grigoriadi
+ */
+@ApplicationScoped
+public class CdiTestService {
+
+    @Inject
+    private Event<MethodCalledEvent> calledEvent;
+
+    @Inject
+    @Hello1
+    private IHelloService helloService;
+
+    public CdiTestService() {
+    }
+
+    public void runService() {
+        helloService.sayHello();
+        calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".runService"));
+    }
+
+    @PreDestroy
+    public void preDestroy() {
+        calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".preDestroy"));
+    }
+
+    public IHelloService getHelloService() {
+        return helloService;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/Hello1.java b/src/test/java/org/eclipse/yasson/internal/cdi/Hello1.java
new file mode 100644
index 0000000..7d769fc
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/Hello1.java
@@ -0,0 +1,30 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.enterprise.inject.Default;
+import jakarta.inject.Qualifier;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@Default
+@Qualifier
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Hello1 {
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/Hello2.java b/src/test/java/org/eclipse/yasson/internal/cdi/Hello2.java
new file mode 100644
index 0000000..1500197
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/Hello2.java
@@ -0,0 +1,30 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.enterprise.inject.Default;
+import jakarta.inject.Qualifier;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@Default
+@Qualifier
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Hello2 {
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/HelloService1.java b/src/test/java/org/eclipse/yasson/internal/cdi/HelloService1.java
new file mode 100644
index 0000000..9631b16
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/HelloService1.java
@@ -0,0 +1,39 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Event;
+import jakarta.inject.Inject;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@Hello1
+@ApplicationScoped
+public class HelloService1 implements IHelloService {
+
+    @Inject
+    private Event<MethodCalledEvent> calledEvent;
+
+    @Override
+    public void sayHello() {
+        calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".sayHello"));
+    }
+
+    @PreDestroy
+    public void preDestroy() {
+        calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".preDestroy"));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/HelloService2.java b/src/test/java/org/eclipse/yasson/internal/cdi/HelloService2.java
new file mode 100644
index 0000000..ab25c77
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/HelloService2.java
@@ -0,0 +1,43 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.annotation.PreDestroy;
+import jakarta.enterprise.context.Dependent;
+import jakarta.enterprise.event.Event;
+import jakarta.inject.Inject;
+
+/**
+ * Dependant scoped bean, should be destroyed with its wrapper bean.
+ *
+ * @author Roman Grigoriadi
+ */
+@Hello2
+@Dependent
+public class HelloService2 implements IHelloService {
+
+    @Inject
+    private Event<MethodCalledEvent> calledEvent;
+
+    @Override
+    public void sayHello() {
+        calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".sayHello"));
+    }
+
+    @PreDestroy
+    public void preDestroy() {
+        calledEvent.fire(new MethodCalledEvent(getClass().getName() + ".preDestroy"));
+    }
+
+
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/IHelloService.java b/src/test/java/org/eclipse/yasson/internal/cdi/IHelloService.java
new file mode 100644
index 0000000..c9e8075
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/IHelloService.java
@@ -0,0 +1,22 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public interface IHelloService {
+
+    void sayHello();
+
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/JndiBeanManager.java b/src/test/java/org/eclipse/yasson/internal/cdi/JndiBeanManager.java
new file mode 100644
index 0000000..9087ef9
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/JndiBeanManager.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2018, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.el.ELResolver;
+import jakarta.el.ExpressionFactory;
+import jakarta.enterprise.context.spi.Context;
+import jakarta.enterprise.context.spi.Contextual;
+import jakarta.enterprise.context.spi.CreationalContext;
+import jakarta.enterprise.event.Event;
+import jakarta.enterprise.inject.Instance;
+import jakarta.enterprise.inject.spi.AnnotatedField;
+import jakarta.enterprise.inject.spi.AnnotatedMember;
+import jakarta.enterprise.inject.spi.AnnotatedMethod;
+import jakarta.enterprise.inject.spi.AnnotatedParameter;
+import jakarta.enterprise.inject.spi.AnnotatedType;
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.BeanAttributes;
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.enterprise.inject.spi.Decorator;
+import jakarta.enterprise.inject.spi.Extension;
+import jakarta.enterprise.inject.spi.InjectionPoint;
+import jakarta.enterprise.inject.spi.InjectionTarget;
+import jakarta.enterprise.inject.spi.InjectionTargetFactory;
+import jakarta.enterprise.inject.spi.InterceptionFactory;
+import jakarta.enterprise.inject.spi.InterceptionType;
+import jakarta.enterprise.inject.spi.Interceptor;
+import jakarta.enterprise.inject.spi.ObserverMethod;
+import jakarta.enterprise.inject.spi.ProducerFactory;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+public class JndiBeanManager implements BeanManager {
+    @Override
+    public Object getReference(Bean<?> bean, Type beanType, CreationalContext<?> ctx) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Object getInjectableReference(InjectionPoint ij, CreationalContext<?> ctx) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <T> CreationalContext<T> createCreationalContext(Contextual<T> contextual) {
+        return null;
+    }
+
+    @Override
+    public Set<Bean<?>> getBeans(Type beanType, Annotation... qualifiers) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Set<Bean<?>> getBeans(String name) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Bean<?> getPassivationCapableBean(String id) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <X> Bean<? extends X> resolve(Set<Bean<? extends X>> beans) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void validate(InjectionPoint injectionPoint) {
+
+    }
+
+    @Override
+    public <T> Set<ObserverMethod<? super T>> resolveObserverMethods(T event, Annotation... qualifiers) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public List<Decorator<?>> resolveDecorators(Set<Type> types, Annotation... qualifiers) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public List<Interceptor<?>> resolveInterceptors(InterceptionType type, Annotation... interceptorBindings) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public boolean isScope(Class<? extends Annotation> annotationType) {
+        return false;
+    }
+
+    @Override
+    public boolean isNormalScope(Class<? extends Annotation> annotationType) {
+        return false;
+    }
+
+    @Override
+    public boolean isPassivatingScope(Class<? extends Annotation> annotationType) {
+        return false;
+    }
+
+    @Override
+    public boolean isQualifier(Class<? extends Annotation> annotationType) {
+        return false;
+    }
+
+    @Override
+    public boolean isInterceptorBinding(Class<? extends Annotation> annotationType) {
+        return false;
+    }
+
+    @Override
+    public boolean isStereotype(Class<? extends Annotation> annotationType) {
+        return false;
+    }
+
+    @Override
+    public Set<Annotation> getInterceptorBindingDefinition(Class<? extends Annotation> bindingType) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Set<Annotation> getStereotypeDefinition(Class<? extends Annotation> stereotype) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public boolean areQualifiersEquivalent(Annotation qualifier1, Annotation qualifier2) {
+        return false;
+    }
+
+    @Override
+    public boolean areInterceptorBindingsEquivalent(Annotation interceptorBinding1, Annotation interceptorBinding2) {
+        return false;
+    }
+
+    @Override
+    public int getQualifierHashCode(Annotation qualifier) {
+        return 0;
+    }
+
+    @Override
+    public int getInterceptorBindingHashCode(Annotation interceptorBinding) {
+        return 0;
+    }
+
+    @Override
+    public Context getContext(Class<? extends Annotation> scopeType) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Collection<Context> getContexts(Class<? extends Annotation> aClass) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public ELResolver getELResolver() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public ExpressionFactory wrapExpressionFactory(ExpressionFactory expressionFactory) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <T> AnnotatedType<T> createAnnotatedType(Class<T> type) {
+        return null;
+    }
+
+    @Override
+    public <T> InjectionTargetFactory<T> getInjectionTargetFactory(AnnotatedType<T> annotatedType) {
+        return new MockInjectionTargetFactory<>();
+    }
+
+    @Override
+    public <X> ProducerFactory<X> getProducerFactory(AnnotatedField<? super X> field, Bean<X> declaringBean) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <X> ProducerFactory<X> getProducerFactory(AnnotatedMethod<? super X> method, Bean<X> declaringBean) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <T> BeanAttributes<T> createBeanAttributes(AnnotatedType<T> type) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public BeanAttributes<?> createBeanAttributes(AnnotatedMember<?> type) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <T> Bean<T> createBean(BeanAttributes<T> attributes, Class<T> beanClass, InjectionTargetFactory<T> injectionTargetFactory) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <T, X> Bean<T> createBean(BeanAttributes<T> attributes, Class<X> beanClass, ProducerFactory<X> producerFactory) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public InjectionPoint createInjectionPoint(AnnotatedField<?> field) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public InjectionPoint createInjectionPoint(AnnotatedParameter<?> parameter) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <T extends Extension> T getExtension(Class<T> extensionClass) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public <T> InterceptionFactory<T> createInterceptionFactory(CreationalContext<T> ctx, Class<T> clazz) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Event<Object> getEvent() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Instance<Object> createInstance() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public boolean isMatchingBean(Set<Type> set, Set<Annotation> set1, Type type, Set<Annotation> set2) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public boolean isMatchingEvent(Type type, Set<Annotation> set, Type type1, Set<Annotation> set1) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/MethodCalledEvent.java b/src/test/java/org/eclipse/yasson/internal/cdi/MethodCalledEvent.java
new file mode 100644
index 0000000..8bf77e4
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/MethodCalledEvent.java
@@ -0,0 +1,29 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class MethodCalledEvent {
+
+    private final String methodName;
+
+    public MethodCalledEvent(String methodName) {
+        this.methodName = methodName;
+    }
+
+    public String getMethodName() {
+        return methodName;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTarget.java b/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTarget.java
new file mode 100644
index 0000000..a006739
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTarget.java
@@ -0,0 +1,54 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.enterprise.context.spi.CreationalContext;
+import jakarta.enterprise.inject.spi.InjectionPoint;
+import jakarta.enterprise.inject.spi.InjectionTarget;
+import jakarta.json.bind.adapter.JsonbAdapter;
+import java.util.Set;
+
+/**
+ * For JNDI Bean Manager resolution testing purposes.
+ */
+public class MockInjectionTarget implements InjectionTarget<JsonbAdapter> {
+    @Override
+    public void inject(JsonbAdapter instance, CreationalContext<JsonbAdapter> ctx) {
+
+    }
+
+    @Override
+    public void postConstruct(JsonbAdapter instance) {
+
+    }
+
+    @Override
+    public void preDestroy(JsonbAdapter instance) {
+
+    }
+
+    @Override
+    public JsonbAdapter produce(CreationalContext<JsonbAdapter> ctx) {
+        return new NonCdiAdapter();
+    }
+
+    @Override
+    public void dispose(JsonbAdapter instance) {
+
+    }
+
+    @Override
+    public Set<InjectionPoint> getInjectionPoints() {
+        return null;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTargetFactory.java b/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTargetFactory.java
new file mode 100644
index 0000000..3a6d8bc
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/MockInjectionTargetFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.enterprise.inject.spi.Bean;
+import jakarta.enterprise.inject.spi.InjectionTarget;
+import jakarta.enterprise.inject.spi.InjectionTargetFactory;
+
+/**
+ * TODO javadoc
+ */
+public class MockInjectionTargetFactory<T> implements InjectionTargetFactory<T> {
+    @Override
+    public InjectionTarget<T> createInjectionTarget(Bean<T> bean) {
+        return (InjectionTarget<T>) new MockInjectionTarget();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/MockJndiContext.java b/src/test/java/org/eclipse/yasson/internal/cdi/MockJndiContext.java
new file mode 100644
index 0000000..aaab2f3
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/MockJndiContext.java
@@ -0,0 +1,173 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import javax.naming.*;
+import java.util.Hashtable;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class MockJndiContext implements Context {
+    private static final ConcurrentHashMap<String, Object> objects = new ConcurrentHashMap<>();
+
+    @Override
+    public Object lookup(Name name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public Object lookup(String name) throws NamingException {
+        final Object o = objects.get(name);
+        if (o == null) {
+            throw new NamingException("No object found under: "+name);
+        }
+        return o;
+    }
+
+    @Override
+    public void bind(Name name, Object obj) throws NamingException {
+
+    }
+
+    @Override
+    public void bind(String name, Object obj) throws NamingException {
+        objects.put(name, obj);
+    }
+
+    @Override
+    public void rebind(Name name, Object obj) throws NamingException {
+
+    }
+
+    @Override
+    public void rebind(String name, Object obj) throws NamingException {
+
+    }
+
+    @Override
+    public void unbind(Name name) throws NamingException {
+
+    }
+
+    @Override
+    public void unbind(String name) throws NamingException {
+        objects.remove(name);
+    }
+
+    @Override
+    public void rename(Name oldName, Name newName) throws NamingException {
+
+    }
+
+    @Override
+    public void rename(String oldName, String newName) throws NamingException {
+
+    }
+
+    @Override
+    public NamingEnumeration<NameClassPair> list(Name name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public NamingEnumeration<NameClassPair> list(String name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public NamingEnumeration<Binding> listBindings(Name name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public NamingEnumeration<Binding> listBindings(String name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public void destroySubcontext(Name name) throws NamingException {
+
+    }
+
+    @Override
+    public void destroySubcontext(String name) throws NamingException {
+
+    }
+
+    @Override
+    public Context createSubcontext(Name name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public Context createSubcontext(String name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public Object lookupLink(Name name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public Object lookupLink(String name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public NameParser getNameParser(Name name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public NameParser getNameParser(String name) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public Name composeName(Name name, Name prefix) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public String composeName(String name, String prefix) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public Object addToEnvironment(String propName, Object propVal) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public Object removeFromEnvironment(String propName) throws NamingException {
+        return null;
+    }
+
+    @Override
+    public Hashtable<?, ?> getEnvironment() throws NamingException {
+        return null;
+    }
+
+    @Override
+    public void close() throws NamingException {
+
+    }
+
+    @Override
+    public String getNameInNamespace() throws NamingException {
+        return null;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/MockJndiContextFactory.java b/src/test/java/org/eclipse/yasson/internal/cdi/MockJndiContextFactory.java
new file mode 100644
index 0000000..61d51a5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/MockJndiContextFactory.java
@@ -0,0 +1,28 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import java.util.Hashtable;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class MockJndiContextFactory implements InitialContextFactory {
+    @Override
+    public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
+        return new MockJndiContext();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/NonCdiAdapter.java b/src/test/java/org/eclipse/yasson/internal/cdi/NonCdiAdapter.java
new file mode 100644
index 0000000..738a456
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/NonCdiAdapter.java
@@ -0,0 +1,30 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import jakarta.json.bind.adapter.JsonbAdapter;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class NonCdiAdapter implements JsonbAdapter<String, Integer> {
+    @Override
+    public Integer adaptToJson(String obj) throws Exception {
+        return Integer.valueOf(obj);
+    }
+
+    @Override
+    public String adaptFromJson(Integer obj) throws Exception {
+        return String.valueOf(obj);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/cdi/WeldManager.java b/src/test/java/org/eclipse/yasson/internal/cdi/WeldManager.java
new file mode 100644
index 0000000..513a9e0
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/cdi/WeldManager.java
@@ -0,0 +1,47 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.cdi;
+
+import org.eclipse.yasson.internal.components.JsonbComponentInstanceCreatorFactory;
+import org.jboss.weld.environment.se.Weld;
+import org.jboss.weld.environment.se.WeldContainer;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+/**
+ * Starts and Shutdowns Weld container, for CDI testing purposes.
+ *
+ * @author Roman Grigoriadi
+ */
+public class WeldManager {
+
+    private Weld weld;
+
+    private InitialContext initialContext;
+
+
+    public void startWeld(Class... scannedClasses) throws NamingException {
+        weld = new Weld().beanClasses(scannedClasses).disableDiscovery();
+        WeldContainer container = weld.initialize();
+        initialContext = new InitialContext();
+        initialContext.bind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME, container.getBeanManager());
+    }
+
+    public void shutdownWeld() throws NamingException {
+        weld.shutdown();
+        initialContext.unbind(JsonbComponentInstanceCreatorFactory.BEAN_MANAGER_NAME);
+        initialContext.close();
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/concurrent/JsonProcessingResult.java b/src/test/java/org/eclipse/yasson/internal/concurrent/JsonProcessingResult.java
new file mode 100644
index 0000000..8b0c281
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/concurrent/JsonProcessingResult.java
@@ -0,0 +1,43 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.concurrent;
+
+/**
+ * A result produced by marshalling or unmarshalling operation called on jsonb.
+ * @param <T> For marshalling its String, for unmarshalling Customer.
+ */
+class JsonProcessingResult<T> {
+    private final T result;
+
+    private final String dispatcherThreadName;
+
+    private final String jobId;
+
+    public JsonProcessingResult(T result, String dispatcherThreadName, String jobId) {
+        this.result = result;
+        this.dispatcherThreadName = dispatcherThreadName;
+        this.jobId = jobId;
+    }
+
+    public T getResult() {
+        return result;
+    }
+
+    public String getDispatcherThreadName() {
+        return dispatcherThreadName;
+    }
+
+    public String getJobId() {
+        return jobId;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/concurrent/MarshallerTask.java b/src/test/java/org/eclipse/yasson/internal/concurrent/MarshallerTask.java
new file mode 100644
index 0000000..3d886da
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/concurrent/MarshallerTask.java
@@ -0,0 +1,51 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.concurrent;
+
+import org.eclipse.yasson.defaultmapping.specific.model.Customer;
+
+import jakarta.json.bind.Jsonb;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Runs JSON marshalling, returns Json result.
+ */
+class MarshallerTask implements Callable<JsonProcessingResult<MarshallerTaskResult>> {
+
+    private final Jsonb jsonb;
+    private final String jobId;
+    private final MultiTenancyTest.ConfigurationType configurationType;
+    private final CountDownLatch latch;
+    private final Customer customer;
+
+    public MarshallerTask(CountDownLatch latch, MultiTenancyTest.ConfigurationType jsonbConfiguration, String jobId, Customer customer) {
+        this.jsonb = jsonbConfiguration.getJsonbInstance();
+        this.jobId = jobId;
+        this.configurationType = jsonbConfiguration;
+        this.latch = latch;
+        this.customer = customer;
+    }
+
+    @Override
+    public JsonProcessingResult<MarshallerTaskResult> call() throws Exception {
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+        //marshalling will produce wrong json result if JsonbConfig in ThreadLocal JsonbContext will not match expected result
+        String json = jsonb.toJson(customer);
+        return new JsonProcessingResult<>(new MarshallerTaskResult(json, configurationType), Thread.currentThread().getName(), jobId);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/concurrent/MarshallerTaskResult.java b/src/test/java/org/eclipse/yasson/internal/concurrent/MarshallerTaskResult.java
new file mode 100644
index 0000000..3d67b7a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/concurrent/MarshallerTaskResult.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.concurrent;
+
+/**
+ * Wraps a resulted JSON string with jsonb configuration type, to know against which json to check result.
+ */
+class MarshallerTaskResult {
+    private final String producedJson;
+    private final MultiTenancyTest.ConfigurationType configurationType;
+
+    public MarshallerTaskResult(String producedJson, MultiTenancyTest.ConfigurationType configurationType) {
+        this.producedJson = producedJson;
+        this.configurationType = configurationType;
+    }
+
+    public String getProducedJson() {
+        return producedJson;
+    }
+
+    public MultiTenancyTest.ConfigurationType getConfigurationType() {
+        return configurationType;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/concurrent/MultiTenancyTest.java b/src/test/java/org/eclipse/yasson/internal/concurrent/MultiTenancyTest.java
new file mode 100644
index 0000000..8bb992a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/concurrent/MultiTenancyTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.concurrent;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.eclipse.yasson.defaultmapping.specific.CustomerTest;
+import org.eclipse.yasson.defaultmapping.specific.model.Customer;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+import jakarta.json.bind.config.PropertyVisibilityStrategy;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Logger;
+
+/**
+ * Tests consistency along sharing instances of Jsonb with different JsonbConfig between threads.
+ * Tests simulates web server like environment, where threads dispatching http requests are pooled and reused.
+ *
+ * Each of jsonb instances has different JsonbConfig, forcing to produce / parse
+ * different JSON string during marshalling to / unmarshalling from json.
+ *
+ * If ThreadLocal JsonbContext will be inconsistently shared between pooled threads
+ * this test should fail while testing results of calls to shared jsonb instances marshalling / unmarshalling methods.
+ *
+ * @author Roman Grigoriadi
+ */
+public class MultiTenancyTest extends CustomerTest {
+
+    private static Logger logger = Logger.getLogger(MultiTenancyTest.class.getName());
+
+    /**
+     * Expected JSON for defaultJsonBinding instance, default key names.
+     */
+    private static final String DEFAULT_CONFIG_JSON_STRING = "{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"integers\":[0,1],\"listOfListsOfIntegers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"MULTI_TENANCY_TEST\",\"stringIntegerMap\":{\"first\":1,\"second\":2},\"strings\":[\"green\",\"yellow\"]}";
+
+    /**
+     * Expected JSON for customizedJsonBinding instance, configured with LOWER_CASE_WITH_UNDERSCORES property naming strategy key names and also skips some properties according to custom PropertyVisibilityStrategy.
+     */
+    private static final String CUSTOMIZED_CONFIG_JSON_STRING = "{\"addresses\":[{\"street\":{\"name\":\"Zoubkova\",\"number\":111},\"town\":\"Prague\"}],\"age\":33,\"list_of_lists_of_integers\":[[0,1,2],[0,1,2],[0,1,2]],\"name\":\"MULTI_TENANCY_TEST\",\"string_integer_map\":{\"first\":1,\"second\":2}}";
+
+    /**
+     * A size of thread pool. Should be lesser than TOTAL_JOB_COUNT, so that threads are reused for different tasks,
+     * causing more likeness to fail ThreadLocal state managing.
+     */
+    private static final int THREAD_COUNT = 35;
+    /**
+     * A total count of marshalling / unmarshalling operations.
+     */
+    private static final int TOTAL_JOB_COUNT = 2000;
+
+    /**
+     * After results of marshalling / unmarshalling are checked this count is incremented.
+     * When test finishes, this count is required to be equal to TOTAL_JOB_COUNT.
+     */
+    private final AtomicLong resultsCheckedCount = new AtomicLong();
+
+    /**
+     * Latch firing first THREAD_COUNT tasks instantly.
+     */
+    private final static CountDownLatch fireJsonbProcessing = new CountDownLatch(1);
+
+    private static final Jsonb defaultJsonBinding;
+    private static final Jsonb customizedJsonBinding;
+
+    public enum ConfigurationType {
+        DEFAULT(defaultJsonBinding, DEFAULT_CONFIG_JSON_STRING),
+        CUSTOMIZED(customizedJsonBinding, CUSTOMIZED_CONFIG_JSON_STRING);
+
+        ConfigurationType(Jsonb jsonbInstance, String expectedJson) {
+            this.jsonbInstance = jsonbInstance;
+            this.expectedJson = expectedJson;
+        }
+
+        private Jsonb jsonbInstance;
+        private String expectedJson;
+
+        public Jsonb getJsonbInstance() {
+            return jsonbInstance;
+        }
+
+        public String getExpectedJson() {
+            return expectedJson;
+        }
+    }
+
+    /**
+     * Thread pool for JSONB processing.
+     */
+    private static ExecutorService jsonbProcessingThreadPool;
+    private static CompletionService<JsonProcessingResult<MarshallerTaskResult>> marshallingCompletion;
+    private static CompletionService<JsonProcessingResult<Customer>> unmarshallingCompletion;
+
+    /**
+     * Executor for checking results.
+     */
+    private static ExecutorService resultCheckService;
+
+    /**
+     * Jsonb instances configuration initialisation.
+     */
+    static {
+        JsonbConfig defaultConfig = new JsonbConfig();
+
+        JsonbConfig customizedConfig = new JsonbConfig();
+        customizedConfig.setProperty(JsonbConfig.PROPERTY_NAMING_STRATEGY, PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES);
+        customizedConfig.setProperty(JsonbConfig.PROPERTY_VISIBILITY_STRATEGY, new PropertyVisibilityStrategy() {
+            @Override
+            public boolean isVisible(Field field) {
+                final String name = field.getName();
+                return !(name.equals("strings") || name.equals("integers"));
+            }
+
+            @Override
+            public boolean isVisible(Method method) {
+                final String name = method.getName();
+                return !(name.equals("getStrings") || name.equals("getIntegers"));
+            }
+        });
+
+
+        defaultJsonBinding = JsonbBuilder.create(defaultConfig);
+        customizedJsonBinding = JsonbBuilder.create(customizedConfig);
+    }
+
+    @BeforeAll
+    public static void setUp() throws Exception {
+        jsonbProcessingThreadPool = Executors.newFixedThreadPool(THREAD_COUNT);
+        marshallingCompletion = new ExecutorCompletionService<>(jsonbProcessingThreadPool);
+        unmarshallingCompletion = new ExecutorCompletionService<>(jsonbProcessingThreadPool);
+        resultCheckService = Executors.newFixedThreadPool(2);
+    }
+
+    @Test
+    public void testDataConsistency() throws Exception {
+        submitJsonbProcessingTasks();
+        fireJsonbProcessing.countDown();
+        submitResultCheckingTasks();
+
+
+        jsonbProcessingThreadPool.shutdown();
+        jsonbProcessingThreadPool.awaitTermination(1000, TimeUnit.MILLISECONDS);
+        resultCheckService.shutdown();
+        resultCheckService.awaitTermination(5000, TimeUnit.MILLISECONDS);
+
+        //Final check, that successful task result check count matches count of tasks that were submitted.
+        assertEquals(TOTAL_JOB_COUNT, resultsCheckedCount.get());
+    }
+
+    /**
+     * Picks marshalling / unmarshalling results from completion service queue and check if they
+     * are equal their expected prototypes.
+     *
+     * If ThreadLocal JsonbContext is incorrectly shared between pooled threads, configuration of jsonb instances
+     * would be stale and results will not match.
+     */
+    private void submitResultCheckingTasks() {
+        resultCheckService.execute(new ResultChecker<>(marshallingCompletion) {
+            @Override
+            protected void checkResult(JsonProcessingResult<MarshallerTaskResult> result) {
+                MarshallerTaskResult marshallerResult = result.getResult();
+                //actual check, produced json by marshaller is equal to expected by configuration, which triggered marshalling task.
+                assertEquals(marshallerResult.getConfigurationType().getExpectedJson(), marshallerResult.getProducedJson());
+                resultsCheckedCount.incrementAndGet();
+                logger.fine(String.format("Job %-32s dispatched by thread %-16s completed successfully.", result.getJobId(), result.getDispatcherThreadName()));
+            }
+        });
+
+        resultCheckService.execute(new ResultChecker<>(unmarshallingCompletion) {
+            @Override
+            protected void checkResult(JsonProcessingResult<Customer> result) {
+                //actual check, unmarshalled json result have all expected values.
+                //if JsonbConfig is stale, key names will not match, and this assert will not pass.
+                assertCustomerValues(result.getResult(), "MULTI_TENANCY_TEST");
+                resultsCheckedCount.incrementAndGet();
+                logger.fine(String.format("Job %-32s dispatched by thread %-16s completed successfully.", result.getJobId(), result.getDispatcherThreadName()));
+            }
+        });
+    }
+
+    /**
+     * Chooses shared jsonb instance, either with default or customized key names
+     * and submits marshaller an unmarshaller task for it.
+     */
+    private static void submitJsonbProcessingTasks() {
+        for(int i = 0; i< TOTAL_JOB_COUNT; i+=2) {
+            boolean even = (i % 4 == 0);
+            ConfigurationType jsonbConfiguration = even ? ConfigurationType.DEFAULT : ConfigurationType.CUSTOMIZED;
+
+            final String unmarshallerJobId = jsonbConfiguration.name() + "_Unmarshaller_ID_" + i;
+            final UnmarshallerTask task = new UnmarshallerTask(fireJsonbProcessing, jsonbConfiguration.getExpectedJson(), jsonbConfiguration.getJsonbInstance(), unmarshallerJobId);
+            unmarshallingCompletion.submit(task);
+
+            final String marshallerJobId = jsonbConfiguration.name() + "_Marshaller_ID_" + i;
+            final MarshallerTask marshallerTask = new MarshallerTask(fireJsonbProcessing, jsonbConfiguration, marshallerJobId, createCustomer("MULTI_TENANCY_TEST"));
+            marshallingCompletion.submit(marshallerTask);
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/concurrent/ResultChecker.java b/src/test/java/org/eclipse/yasson/internal/concurrent/ResultChecker.java
new file mode 100644
index 0000000..87f25e7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/concurrent/ResultChecker.java
@@ -0,0 +1,47 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.concurrent;
+
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Checks result of marshalling / unmarshalling operation.
+ */
+abstract class ResultChecker<T> implements Runnable {
+
+    private final CompletionService<JsonProcessingResult<T>> completionService;
+
+    public ResultChecker(CompletionService<JsonProcessingResult<T>> completionService) {
+        this.completionService = completionService;
+    }
+
+    /**
+     * Polls Callable results from CompletionService result queue and checks validity.
+     */
+    @Override
+    public void run() {
+        Future<JsonProcessingResult<T>> resultFuture;
+        try {
+            while ((resultFuture = completionService.poll(500, TimeUnit.MILLISECONDS)) != null) {
+                checkResult(resultFuture.get());
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected abstract void checkResult(JsonProcessingResult<T> result);
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/concurrent/UnmarshallerTask.java b/src/test/java/org/eclipse/yasson/internal/concurrent/UnmarshallerTask.java
new file mode 100644
index 0000000..5ba424c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/concurrent/UnmarshallerTask.java
@@ -0,0 +1,48 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.concurrent;
+
+import org.eclipse.yasson.defaultmapping.specific.model.Customer;
+
+import jakarta.json.bind.Jsonb;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Runs Jsonb unmarshaller, returns result.
+ */
+class UnmarshallerTask implements Callable<JsonProcessingResult<Customer>> {
+
+    private final String json;
+
+    private final Jsonb jsonb;
+
+    private final String jobId;
+
+    private final CountDownLatch latch;
+
+    public UnmarshallerTask(CountDownLatch latch, String json, Jsonb jsonb, String jobId) {
+        this.json = json;
+        this.jsonb = jsonb;
+        this.jobId = jobId;
+        this.latch = latch;
+    }
+
+    @Override
+    public JsonProcessingResult<Customer> call() throws Exception {
+        latch.await();
+        //unmarshalling will fail if JsonbConfig in ThreadLocal JsonbContext will not match expected json
+        Customer customer = jsonb.fromJson(json, Customer.class);
+        return new JsonProcessingResult<>(customer, Thread.currentThread().getName(), jobId);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/model/ModulesUtil.java b/src/test/java/org/eclipse/yasson/internal/model/ModulesUtil.java
new file mode 100644
index 0000000..094b629
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/model/ModulesUtil.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2021, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model;
+
+import java.lang.invoke.MethodHandles;
+
+/**
+ * Why is this class here?.
+ *
+ * This class is here to replace existing classes in test-runtime:
+ * - src/main/java/org/eclipse/yasson/internal/model/ModulesUtil.java
+ * - src/main/java9/org/eclipse/yasson/internal/model/ModulesUtil.java
+ *
+ * When tests are executed with maven-surefire-plugin the content of
+ * 'classes' is in a different module-path than 'test-classes'.
+ *
+ * This causes the MethodHandles#publicLookup to fail. The reason is that
+ * test classes to serialize/deserialize are coming from module 'test-classes'
+ * and the module 'classes' has no access to it. The 'publicLookup' makes some
+ * validations and because of this different modules, it fails.
+ *
+ * It should work if 'classes' and 'test-classes' are merged in one unique module.
+ *
+ */
+class ModulesUtil {
+
+
+    private ModulesUtil() {
+    }
+
+    static MethodHandles.Lookup lookup(){
+        return MethodHandles.lookup();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/model/customization/naming/NamingPojo.java b/src/test/java/org/eclipse/yasson/internal/model/customization/naming/NamingPojo.java
new file mode 100644
index 0000000..a4f2365
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/model/customization/naming/NamingPojo.java
@@ -0,0 +1,32 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization.naming;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class NamingPojo {
+
+    public NamingPojo() {
+    }
+
+    public NamingPojo(String upperCasedProperty, String _startingWithUnderscoreProperty, String CAPS_UNDERSCORE_PROPERTY) {
+        this.upperCasedProperty = upperCasedProperty;
+        this._startingWithUnderscoreProperty = _startingWithUnderscoreProperty;
+        this.CAPS_UNDERSCORE_PROPERTY = CAPS_UNDERSCORE_PROPERTY;
+    }
+
+    public String upperCasedProperty;
+    public String _startingWithUnderscoreProperty;
+    public String CAPS_UNDERSCORE_PROPERTY;
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/model/customization/naming/PropertyNamingStrategyTest.java b/src/test/java/org/eclipse/yasson/internal/model/customization/naming/PropertyNamingStrategyTest.java
new file mode 100644
index 0000000..33c0c60
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/model/customization/naming/PropertyNamingStrategyTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.model.customization.naming;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+
+import org.eclipse.yasson.internal.model.customization.StrategiesProvider;
+
+/**
+ * Tests naming strategies.
+ *
+ * @author Roman Grigoriadi
+ */
+public class PropertyNamingStrategyTest {
+
+    private final NamingPojo pojo = new NamingPojo("abc", "def", "ghi");
+
+    @Test
+    public void testLowerCase() throws Exception {
+        PropertyNamingStrategy strategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES);
+        assertEquals("camel_case_property", strategy.translateName("camelCaseProperty"));
+        assertEquals("camelcase_property", strategy.translateName("CamelcaseProperty"));
+        assertEquals("camel_case_property", strategy.translateName("CamelCaseProperty"));
+        assertEquals("_camel_case_property", strategy.translateName("_camelCaseProperty"));
+        assertEquals("_camel_case_property", strategy.translateName("_CamelCaseProperty"));
+
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES));
+        String lowercaseUnderscoresJson = "{\"_starting_with_underscore_property\":\"def\",\"caps_underscore_property\":\"ghi\",\"upper_cased_property\":\"abc\"}";
+        assertEquals(lowercaseUnderscoresJson, jsonb.toJson(pojo));
+        NamingPojo result = jsonb.fromJson(lowercaseUnderscoresJson, NamingPojo.class);
+        assertResult(result);
+    }
+
+    @Test
+    public void testLowerDashes() throws Exception {
+        PropertyNamingStrategy strategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES);
+        assertEquals("camel-case-property", strategy.translateName("camelCaseProperty"));
+        assertEquals("camelcase-property", strategy.translateName("CamelcaseProperty"));
+        assertEquals("camel-case-property", strategy.translateName("CamelCaseProperty"));
+        assertEquals("-camel-case-property", strategy.translateName("-camelCaseProperty"));
+        assertEquals("-camel-case-property", strategy.translateName("-CamelCaseProperty"));
+
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES));
+        String lowercaseDashesJson = "{\"_starting-with-underscore-property\":\"def\",\"caps_underscore_property\":\"ghi\",\"upper-cased-property\":\"abc\"}";
+        assertEquals(lowercaseDashesJson, jsonb.toJson(pojo));
+        NamingPojo result = jsonb.fromJson(lowercaseDashesJson, NamingPojo.class);
+        assertResult(result);
+    }
+
+    @Test
+    public void testUpperCase() {
+        PropertyNamingStrategy upperCaseStrategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
+        assertEquals("UpperCamelCase", upperCaseStrategy.translateName("upperCamelCase"));
+        assertEquals("UpperCamelCase", upperCaseStrategy.translateName("UpperCamelCase"));
+
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE));
+        String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"UpperCasedProperty\":\"abc\",\"_startingWithUnderscoreProperty\":\"def\"}";
+        assertEquals(upperCased, jsonb.toJson(pojo));
+        NamingPojo result = jsonb.fromJson(upperCased, NamingPojo.class);
+        assertResult(result);
+    }
+
+    @Test
+    public void testUpperCaseWithSpaces() {
+        PropertyNamingStrategy upperCaseWithSpacesStrategy = StrategiesProvider.getPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES);
+        assertEquals("Upper Camel Case", upperCaseWithSpacesStrategy.translateName("upperCamelCase"));
+        assertEquals("Upper Camel Case", upperCaseWithSpacesStrategy.translateName("UpperCamelCase"));
+
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE_WITH_SPACES));
+        String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"Upper Cased Property\":\"abc\",\"_starting With Underscore Property\":\"def\"}";
+        assertEquals(upperCased, jsonb.toJson(pojo));
+        NamingPojo result = jsonb.fromJson(upperCased, NamingPojo.class);
+        assertResult(result);
+    }
+
+    @Test
+    public void testCaseInsensitive() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.CASE_INSENSITIVE));
+        String upperCased = "{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty\":\"def\",\"upperCasedProperty\":\"abc\"}";
+        assertEquals(upperCased, jsonb.toJson(pojo));
+        NamingPojo result = jsonb.fromJson("{\"caPS_unDERscore_prOPERty\":\"ghi\",\"_startingwithUndERSCorePrOPERTy\":\"def\",\"upPERCASedProPerty\":\"abc\"}", NamingPojo.class);
+        assertResult(result);
+    }
+    
+    @Test
+    public void testIdentityCaseSensitive() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(PropertyNamingStrategy.IDENTITY));
+        NamingPojo result = jsonb.fromJson("{\"CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty\":\"def\",\"UPPERCASEDPROPERTY\":\"abc\"}", NamingPojo.class);
+        assertEquals("ghi", result.CAPS_UNDERSCORE_PROPERTY);
+        assertEquals("def", result._startingWithUnderscoreProperty);
+        assertNull(result.upperCasedProperty);
+    }
+
+    @Test
+    public void testCustom() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyNamingStrategy(propertyName -> propertyName + "_" + propertyName.toUpperCase()));
+
+        String custom = "{\"CAPS_UNDERSCORE_PROPERTY_CAPS_UNDERSCORE_PROPERTY\":\"ghi\",\"_startingWithUnderscoreProperty__STARTINGWITHUNDERSCOREPROPERTY\":\"def\",\"upperCasedProperty_UPPERCASEDPROPERTY\":\"abc\"}";
+        assertEquals(custom, jsonb.toJson(pojo));
+        NamingPojo result = jsonb.fromJson(custom, NamingPojo.class);
+        assertResult(result);
+    }
+    
+    private static void assertResult(NamingPojo result) {
+        assertEquals("abc", result.upperCasedProperty);
+        assertEquals("def", result._startingWithUnderscoreProperty);
+        assertEquals("ghi", result.CAPS_UNDERSCORE_PROPERTY);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/internal/serializer/ObjectDeserializerTest.java b/src/test/java/org/eclipse/yasson/internal/serializer/ObjectDeserializerTest.java
new file mode 100644
index 0000000..3c41581
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/internal/serializer/ObjectDeserializerTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.internal.serializer;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import jakarta.json.bind.JsonbException;
+
+public class ObjectDeserializerTest {
+
+    @Test
+    public void testGetInstanceExceptionShouldContainClassNameOnMissingConstructor() {
+        assertThrows(JsonbException.class, 
+                () -> defaultJsonb.fromJson("{\"key\":\"value\"}", DummyDeserializationClass.class),
+                DummyDeserializationClass.class::getName);
+    }
+
+    public static class DummyDeserializationClass {
+        private String key;
+
+        public DummyDeserializationClass(String key) {
+            this.key = key;
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public void setKey(String key) {
+            this.key = key;
+        }
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonpsubstitution/AdaptedJsonParser.java b/src/test/java/org/eclipse/yasson/jsonpsubstitution/AdaptedJsonParser.java
new file mode 100644
index 0000000..3edd5c3
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonpsubstitution/AdaptedJsonParser.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonpsubstitution;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonLocation;
+import jakarta.json.stream.JsonParser;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.stream.Stream;
+
+public class AdaptedJsonParser implements JsonParser {
+
+    /**
+     * Adapts every parsed string value.
+     */
+    public interface StringValueAdapter {
+        String adaptStringValue(String value);
+    }
+
+    private final StringValueAdapter adapter;
+
+    private final JsonParser jsonParser;
+
+    public AdaptedJsonParser(StringValueAdapter adapter, InputStream inputStream) {
+        this.adapter = adapter;
+        this.jsonParser = Json.createParser(inputStream);
+    }
+
+    @Override
+    public boolean hasNext() {
+        return jsonParser.hasNext();
+    }
+
+    @Override
+    public Event next() {
+        return jsonParser.next();
+    }
+
+    @Override
+    public String getString() {
+        return adapter.adaptStringValue(jsonParser.getString());
+    }
+
+    @Override
+    public boolean isIntegralNumber() {
+        return jsonParser.isIntegralNumber();
+    }
+
+    @Override
+    public int getInt() {
+        return jsonParser.getInt();
+    }
+
+    @Override
+    public long getLong() {
+        return jsonParser.getLong();
+    }
+
+    @Override
+    public BigDecimal getBigDecimal() {
+        return jsonParser.getBigDecimal();
+    }
+
+    @Override
+    public JsonLocation getLocation() {
+        return jsonParser.getLocation();
+    }
+
+    @Override
+    public JsonObject getObject() {
+        return jsonParser.getObject();
+    }
+
+    @Override
+    public JsonValue getValue() {
+        return jsonParser.getValue();
+    }
+
+    @Override
+    public JsonArray getArray() {
+        return jsonParser.getArray();
+    }
+
+    @Override
+    public Stream<JsonValue> getArrayStream() {
+        return jsonParser.getArrayStream();
+    }
+
+    @Override
+    public Stream<Map.Entry<String, JsonValue>> getObjectStream() {
+        return jsonParser.getObjectStream();
+    }
+
+    @Override
+    public Stream<JsonValue> getValueStream() {
+        return jsonParser.getValueStream();
+    }
+
+    @Override
+    public void skipArray() {
+        jsonParser.skipArray();
+    }
+
+    @Override
+    public void skipObject() {
+        jsonParser.skipObject();
+    }
+
+    @Override
+    public void close() {
+        jsonParser.close();
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonpsubstitution/PreinstantiatedJsonpTest.java b/src/test/java/org/eclipse/yasson/jsonpsubstitution/PreinstantiatedJsonpTest.java
new file mode 100644
index 0000000..93c02bb
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonpsubstitution/PreinstantiatedJsonpTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonpsubstitution;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.Assertions;
+import org.eclipse.yasson.TestTypeToken;
+
+import jakarta.json.bind.JsonbException;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+
+public class PreinstantiatedJsonpTest {
+
+    public static class Dog {
+
+        public String name;
+        public int age;
+        public boolean goodDog = true;
+
+        public Dog() {
+        }
+
+        public Dog(String name, int age) {
+            this.name = name;
+            this.age = age;
+        }
+
+        @Override
+        public String toString() {
+            return "Dog:\nname: " + name + "\nage: " + age + "\ngoodDog: " + goodDog;
+        }
+    }
+
+    public static class Wrapper<T> {
+        private T value;
+
+        public T getValue() {
+            return value;
+        }
+
+        public void setValue(T value) {
+            this.value = value;
+        }
+    }
+
+    private final String EXPECTED_JSON = "{\"age\":4,\"goodDog\":true,\"name\":\"Falco\",\"suffix\":\"Best dog ever!\"}";
+
+    private final String WRAPPED_JSON = "{\"instance\":" + EXPECTED_JSON + "}";
+
+    private Dog dog = new Dog("Falco", 4);
+
+    @Test
+    public void testPreinstantiatedJsonGeneratorAndParser() {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        JsonGenerator generator = new SuffixJsonGenerator("Best dog ever!", out);
+        bindingYassonJsonb.toJson(dog, generator);
+        generator.close();
+
+        assertEquals(EXPECTED_JSON, new String(out.toByteArray()));
+
+        ByteArrayInputStream in = new ByteArrayInputStream(EXPECTED_JSON.getBytes());
+        JsonParser parser = new AdaptedJsonParser((value) -> {
+            if ("Falco".equals(value)) {
+                return value + ", a best dog ever!";
+            }
+            return value;
+        }, in);
+        Dog result = bindingYassonJsonb.fromJson(parser, Dog.class);
+
+        assertEquals("Falco, a best dog ever!", result.name);
+        assertEquals(4, result.age);
+        assertTrue(result.goodDog);
+    }
+
+    @Test
+    public void testJsonParserAdvancedToCustomPosition() {
+        ByteArrayInputStream in = new ByteArrayInputStream(WRAPPED_JSON.getBytes());
+        JsonParser parser = new AdaptedJsonParser((value) -> {
+            if ("Falco".equals(value)) {
+                return value + ", a best dog ever!";
+            }
+            return value;
+        }, in);
+
+        parser.next(); //START_OBJECT
+        parser.next(); //"instance" KEY
+
+        Dog result = bindingYassonJsonb.fromJson(parser, Dog.class);
+
+        parser.next(); //END_OJBECT
+
+        assertEquals("Falco, a best dog ever!", result.name);
+        assertEquals(4, result.age);
+        assertTrue(result.goodDog);
+    }
+
+    @Test
+    public void testGeneratorWrappedWithUserInteraction() {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        JsonGenerator generator = new SuffixJsonGenerator("Best dog ever!", out);
+
+        generator.writeStartObject();
+        generator.writeKey("instance");
+        bindingYassonJsonb.toJson(dog, generator);
+        generator.writeEnd();
+        generator.close();
+
+        assertEquals(WRAPPED_JSON, new String(out.toByteArray()));
+    }
+
+    @Test
+    public void testInvalidJsonParserAdvancedToCustomPosition() {
+        ByteArrayInputStream in = new ByteArrayInputStream(WRAPPED_JSON.getBytes());
+        JsonParser parser = new AdaptedJsonParser((value) -> {
+            if ("Falco".equals(value)) {
+                return value + ", a best dog ever!";
+            }
+            return value;
+        }, in);
+
+        parser.next(); //START_OBJECT
+        //should be advanced further
+
+        try {
+        	bindingYassonJsonb.fromJson(parser, Dog.class);
+            fail("JsonbException not thrown");
+        } catch (JsonbException e) {
+            //OK, parser in inconsistent state
+        }
+    }
+
+    @Test
+    public void testInvalidGeneratorWrappedWithUserInteraction() {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        JsonGenerator generator = new SuffixJsonGenerator("Best dog ever!", out);
+
+        generator.writeStartObject();
+        //key not written
+
+        Assertions.shouldFail(() -> bindingYassonJsonb.toJson(dog, generator));
+    }
+
+    @Test
+    public void testRuntimeTypeParser() {
+        Wrapper<String> stringWrapper = new Wrapper<>();
+        stringWrapper.setValue("String value");
+        ByteArrayInputStream in = new ByteArrayInputStream("{\"value\":\"String value\"}".getBytes());
+        JsonParser parser = new AdaptedJsonParser((value) -> {
+            if (value.equals("String value")) {
+                return "Adapted string";
+            }
+            return value;
+        }, in);
+        Wrapper<String> result = bindingYassonJsonb.fromJson(parser, new TestTypeToken<Wrapper<String>>() {}.getType());
+        assertEquals("Adapted string", result.getValue());
+    }
+
+    /**
+     * This test tests that provided generator is actually used.
+     */
+    @Test
+    public void testRuntimeTypeGenerator() {
+        Wrapper<String> stringWrapper = new Wrapper<>();
+        stringWrapper.setValue("String value");
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        JsonGenerator generator = new SuffixJsonGenerator("Appended value.", out);
+        bindingYassonJsonb.toJson(stringWrapper, new TestTypeToken<Wrapper<String>>(){}.getType(), generator);
+        generator.close();
+        assertEquals("{\"value\":\"String value\",\"suffix\":\"Appended value.\"}", out.toString());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonpsubstitution/SuffixJsonGenerator.java b/src/test/java/org/eclipse/yasson/jsonpsubstitution/SuffixJsonGenerator.java
new file mode 100644
index 0000000..078d393
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonpsubstitution/SuffixJsonGenerator.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonpsubstitution;
+
+import jakarta.json.Json;
+import jakarta.json.JsonValue;
+import jakarta.json.stream.JsonGenerator;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Test purpose generator appending a suffix key-value to end of object.
+ */
+public class SuffixJsonGenerator implements JsonGenerator {
+
+    private final JsonGenerator generator;
+
+    /**
+     * Create key-value pair for each object at its end.
+     */
+    private final String objSuffix;
+
+    private boolean written;
+
+    public SuffixJsonGenerator(String objSuffix, OutputStream out) {
+        this.objSuffix = objSuffix;
+        this.generator = Json.createGenerator(out);
+    }
+
+    @Override
+    public JsonGenerator writeStartObject() {
+        return generator.writeStartObject();
+    }
+
+    @Override
+    public JsonGenerator writeStartObject(String name) {
+        return generator.writeStartObject(name);
+    }
+
+    @Override
+    public JsonGenerator writeKey(String name) {
+        return generator.writeKey(name);
+    }
+
+    @Override
+    public JsonGenerator writeStartArray() {
+        return generator.writeStartArray();
+    }
+
+    @Override
+    public JsonGenerator writeStartArray(String name) {
+        return generator.writeStartArray(name);
+    }
+
+    @Override
+    public JsonGenerator write(String name, JsonValue value) {
+        return generator.write(name, value);
+    }
+
+    @Override
+    public JsonGenerator write(String name, String value) {
+        return generator.write(name, value);
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigInteger value) {
+        return generator.write(name, value);
+    }
+
+    @Override
+    public JsonGenerator write(String name, BigDecimal value) {
+        return generator.write(name, value);
+    }
+
+    @Override
+    public JsonGenerator write(String name, int value) {
+        return generator.write(name, value);
+    }
+
+    @Override
+    public JsonGenerator write(String name, long value) {
+        return generator.write(name, value);
+    }
+
+    @Override
+    public JsonGenerator write(String name, double value) {
+        return generator.write(name, value);
+    }
+
+    @Override
+    public JsonGenerator write(String name, boolean value) {
+        return generator.write(name, value);
+    }
+
+    @Override
+    public JsonGenerator writeNull(String name) {
+        return generator.writeNull(name);
+    }
+
+    @Override
+    public JsonGenerator writeEnd() {
+        if (!written) {
+            generator.write("suffix", objSuffix);
+            written = true;
+        }
+        return generator.writeEnd();
+    }
+
+    @Override
+    public JsonGenerator write(JsonValue value) {
+        return generator.write(value);
+    }
+
+    @Override
+    public JsonGenerator write(String value) {
+        return generator.write(value);
+    }
+
+    @Override
+    public JsonGenerator write(BigDecimal value) {
+        return generator.write(value);
+    }
+
+    @Override
+    public JsonGenerator write(BigInteger value) {
+        return generator.write(value);
+    }
+
+    @Override
+    public JsonGenerator write(int value) {
+        return generator.write(value);
+    }
+
+    @Override
+    public JsonGenerator write(long value) {
+        return generator.write(value);
+    }
+
+    @Override
+    public JsonGenerator write(double value) {
+        return generator.write(value);
+    }
+
+    @Override
+    public JsonGenerator write(boolean value) {
+        return generator.write(value);
+    }
+
+    @Override
+    public JsonGenerator writeNull() {
+        return generator.writeNull();
+    }
+
+    @Override
+    public void close() {
+        generator.close();
+    }
+
+    @Override
+    public void flush() {
+        generator.flush();
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojo.java b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojo.java
new file mode 100644
index 0000000..a0f6c3c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojo.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonstructure;
+
+public final class InnerPojo {
+    private String innerFirst;
+    private String innerSecond;
+
+    public String getInnerFirst() {
+        return innerFirst;
+    }
+
+    public void setInnerFirst(String innerFirst) {
+        this.innerFirst = innerFirst;
+    }
+
+    public String getInnerSecond() {
+        return innerSecond;
+    }
+
+    public void setInnerSecond(String innerSecond) {
+        this.innerSecond = innerSecond;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoDeserializer.java b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoDeserializer.java
new file mode 100644
index 0000000..ab6626d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoDeserializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonstructure;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+
+public class InnerPojoDeserializer implements JsonbDeserializer<InnerPojo> {
+    @Override
+    public InnerPojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        InnerPojo innerPojo = new InnerPojo();
+        //KEY first
+        parser.next();
+        //VALUE
+        parser.next();
+        innerPojo.setInnerFirst(parser.getString());
+        //KEY second
+        parser.next();
+        //VALUE
+        parser.next();
+        innerPojo.setInnerSecond(parser.getString());
+        //END_OBJECT
+        parser.next();
+        return innerPojo;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoSerializer.java b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoSerializer.java
new file mode 100644
index 0000000..483b929
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/InnerPojoSerializer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonstructure;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+public class InnerPojoSerializer implements JsonbSerializer<InnerPojo> {
+
+    @Override
+    public void serialize(InnerPojo obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.write("first", obj.getInnerFirst());
+        generator.write("second", obj.getInnerSecond());
+        generator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java b/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java
new file mode 100644
index 0000000..f9771c6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/JsonGeneratorToStructureAdapterTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonstructure;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.YassonJsonb;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonNumber;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonString;
+import jakarta.json.JsonStructure;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+public class JsonGeneratorToStructureAdapterTest {
+
+    @Test
+    public void testSimplePojo() {
+        Pojo pojo = new Pojo();
+        pojo.setBigDecimalProperty(BigDecimal.TEN);
+        pojo.setLongProperty(10L);
+        pojo.setStringProperty("String value");
+
+        JsonObject result = (JsonObject) yassonJsonb.toJsonStructure(pojo);
+
+        assertEquals("String value", getString(result.get("stringProperty")));
+        JsonValue bigDecimalProperty = result.get("bigDecimalProperty");
+        assertEquals(JsonValue.ValueType.NUMBER, bigDecimalProperty.getValueType());
+        assertEquals(BigDecimal.TEN, ((JsonNumber) bigDecimalProperty).bigDecimalValue());
+        JsonValue longProperty = result.get("longProperty");
+        assertEquals(JsonValue.ValueType.NUMBER, longProperty.getValueType());
+        assertEquals(10L, ((JsonNumber) longProperty).longValueExact());
+    }
+
+    @Test
+    public void testInnerObjects() {
+        Pojo pojo = new Pojo();
+        pojo.setBigDecimalProperty(BigDecimal.TEN);
+        pojo.setLongProperty(10L);
+        pojo.setStringProperty("String value");
+        pojo.setInner(new InnerPojo());
+        pojo.getInner().setInnerFirst("First");
+        pojo.getInner().setInnerSecond("Second");
+
+        JsonObject result = (JsonObject) yassonJsonb.toJsonStructure(pojo, Pojo.class);
+        assertEquals("String value", getString(result.get("stringProperty")));
+        JsonValue bigDecimalProperty = result.get("bigDecimalProperty");
+        assertTrue(bigDecimalProperty instanceof JsonNumber);
+        assertEquals(BigDecimal.TEN, ((JsonNumber) bigDecimalProperty).bigDecimalValue());
+        JsonValue longProperty = result.get("longProperty");
+        assertTrue(longProperty instanceof JsonNumber);
+        assertEquals(10L, ((JsonNumber) longProperty).longValueExact());
+
+        JsonValue inner = result.get("inner");
+        assertEquals(JsonValue.ValueType.OBJECT, inner.getValueType());
+        assertEquals("First", ((JsonObject)inner).getString("innerFirst"));
+        assertEquals("Second", ((JsonObject)inner).getString("innerSecond"));
+    }
+
+    @Test
+    public void testSimpleJsonArray() {
+        List<Object> objList = new ArrayList<>();
+        objList.add("First");
+        objList.add(10L);
+        objList.add(BigDecimal.ONE);
+        objList.add(Boolean.TRUE);
+        objList.add(null);
+
+        JsonArray result = (JsonArray) yassonJsonb.toJsonStructure(objList);
+        assertEquals("First", result.getString(0));
+        assertEquals(10L, result.getJsonNumber(1).longValueExact());
+        assertEquals(BigDecimal.ONE, result.getJsonNumber(2).bigDecimalValue());
+        assertEquals(Boolean.TRUE, result.getBoolean(3));
+        assertEquals(JsonValue.ValueType.NULL, result.get(4).getValueType());
+    }
+
+    @Test
+    public void testJsonArrayInJsonObject() {
+        Pojo pojo = new Pojo();
+        pojo.setStrings(new ArrayList<>());
+        pojo.setBigDecimals(new ArrayList<>());
+        pojo.setBooleans(new ArrayList<>());
+        pojo.getStrings().add("First");
+        pojo.getBigDecimals().add(BigDecimal.TEN);
+        pojo.getBooleans().add(Boolean.TRUE);
+
+        JsonObject result = (JsonObject) yassonJsonb.toJsonStructure(pojo);
+        assertEquals(JsonValue.ValueType.ARRAY, result.get("strings").getValueType());
+        assertEquals(JsonValue.ValueType.ARRAY, result.get("bigDecimals").getValueType());
+        assertEquals(JsonValue.ValueType.ARRAY, result.get("booleans").getValueType());
+        assertEquals("First", result.getJsonArray("strings").getString(0));
+        assertEquals(BigDecimal.TEN, result.getJsonArray("bigDecimals").getJsonNumber(0).bigDecimalValue());
+        assertEquals(Boolean.TRUE, result.getJsonArray("booleans").getBoolean(0));
+    }
+
+    @Test
+    public void testNestedJsonArrays() {
+        List<List<Object>> outer = new ArrayList<>();
+        List<Object> inner = new ArrayList<>();
+        inner.add("First");
+        inner.add(10L);
+        inner.add(BigDecimal.ONE);
+        inner.add(Boolean.TRUE);
+        inner.add(null);
+        outer.add(inner);
+
+        JsonArray result = (JsonArray) yassonJsonb.toJsonStructure(outer);
+        assertEquals(JsonValue.ValueType.ARRAY, result.get(0).getValueType());
+        JsonArray resultInner = result.getJsonArray(0);
+
+        assertEquals("First", resultInner.getString(0));
+        assertEquals(10L, resultInner.getJsonNumber(1).longValueExact());
+        assertEquals(BigDecimal.ONE, resultInner.getJsonNumber(2).bigDecimalValue());
+        assertEquals(Boolean.TRUE, resultInner.getBoolean(3));
+        assertEquals(JsonValue.ValueType.NULL, resultInner.get(4).getValueType());
+    }
+
+    @Test
+    public void testCustomJsonbSerializer() {
+        Pojo pojo = new Pojo();
+        pojo.setInner(new InnerPojo());
+        pojo.getInner().setInnerFirst("First value");
+        pojo.getInner().setInnerSecond("Second value");
+        YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create(new JsonbConfig().withSerializers(new InnerPojoSerializer()));
+        JsonStructure result = jsonb.toJsonStructure(pojo);
+        assertEquals(JsonValue.ValueType.OBJECT, result.getValueType());
+        assertEquals(JsonValue.ValueType.OBJECT, ((JsonObject) result).get("inner").getValueType());
+        JsonObject inner = (JsonObject) ((JsonObject) result).get("inner");
+        assertEquals(JsonValue.ValueType.STRING, inner.get("first").getValueType());
+        assertEquals("First value", ((JsonString) inner.get("first")).getString());
+        assertEquals(JsonValue.ValueType.STRING, inner.get("second").getValueType());
+        assertEquals("Second value", ((JsonString) inner.get("second")).getString());
+    }
+
+    private static String getString(JsonValue value) {
+        if (value instanceof JsonString) {
+            return ((JsonString) value).getString();
+        }
+        return value.toString();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java b/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java
new file mode 100644
index 0000000..f6d042a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/JsonStructureToParserAdapterTest.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonstructure;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.eclipse.yasson.Jsonbs.*;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.YassonJsonb;
+
+import jakarta.json.JsonArray;
+import jakarta.json.JsonArrayBuilder;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonObjectBuilder;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.spi.JsonProvider;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class JsonStructureToParserAdapterTest {
+    private final JsonProvider jsonProvider = JsonProvider.provider();
+
+    @Test
+    public void testBasicJsonObject() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class);
+        assertEquals("value 1", result.getStringProperty());
+        assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), result.getLongProperty());
+    }
+
+    @Test
+    public void testNullValues() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        objectBuilder.addNull("stringProperty");
+        objectBuilder.addNull("bigDecimalProperty");
+        objectBuilder.add("longProperty", 10L);
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class);
+        assertNull(result.getStringProperty());
+        assertNull(result.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), result.getLongProperty());
+    }
+
+    @Test
+    public void testInnerJsonObjectWrappedWithProperties() {
+        JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder();
+        innerBuilder.add("innerFirst", "Inner value 1");
+        innerBuilder.add("innerSecond", "Inner value 2");
+
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("inner", innerBuilder.build());
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class);
+
+        assertEquals("value 1", result.getStringProperty());
+        assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), result.getLongProperty());
+        assertEquals("Inner value 1", result.getInner().getInnerFirst());
+        assertEquals("Inner value 2", result.getInner().getInnerSecond());
+    }
+
+    @Test
+    public void testInnerJsonObjectAtEndProperty() {
+        JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder();
+        innerBuilder.add("innerFirst", "Inner value 1");
+        innerBuilder.add("innerSecond", "Inner value 2");
+
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+        objectBuilder.add("inner", innerBuilder.build());
+
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class);
+
+        assertEquals("value 1", result.getStringProperty());
+        assertEquals(new BigDecimal("1.1"), result.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), result.getLongProperty());
+        assertEquals("Inner value 1", result.getInner().getInnerFirst());
+        assertEquals("Inner value 2", result.getInner().getInnerSecond());
+
+    }
+
+    @Test
+    public void testEmptyJsonObject() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        JsonObject jsonObject = objectBuilder.build();
+        Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class);
+        assertNull(result.getStringProperty());
+        assertNull(result.getBigDecimalProperty());
+        assertNull(result.getLongProperty());
+    }
+
+    @Test
+    public void testEmptyInnerJsonObject() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+
+        JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder();
+        JsonObject innerObject = innerBuilder.build();
+
+        objectBuilder.add("inner", innerObject);
+
+        JsonObject jsonObject = objectBuilder.build();
+
+        Pojo result = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class);
+        assertNull(result.getStringProperty());
+        assertNull(result.getBigDecimalProperty());
+        assertNull(result.getLongProperty());
+
+        assertNotNull(result.getInner());
+        assertNull(result.getInner().getInnerFirst());
+        assertNull(result.getInner().getInnerSecond());
+    }
+
+    @Test
+    public void testSimpleArray() {
+        JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder();
+        arrayBuilder.add(BigDecimal.TEN).add("String value").addNull();
+        JsonArray jsonArray = arrayBuilder.build();
+        List result = yassonJsonb.fromJsonStructure(jsonArray, ArrayList.class);
+        assertEquals(3, result.size());
+        assertEquals(BigDecimal.TEN, result.get(0));
+        assertEquals("String value", result.get(1));
+        assertNull(result.get(2));
+    }
+
+    @Test
+    public void testArraysInsideObject() {
+        JsonArrayBuilder bigDecBuilder = jsonProvider.createArrayBuilder();
+        JsonArrayBuilder strBuilder = jsonProvider.createArrayBuilder();
+        JsonArrayBuilder blnBuilder = jsonProvider.createArrayBuilder();
+
+        bigDecBuilder.add(BigDecimal.TEN);
+        strBuilder.add("String value 1");
+        blnBuilder.add(Boolean.TRUE);
+
+        JsonObjectBuilder pojoBuilder = jsonProvider.createObjectBuilder();
+        pojoBuilder.add("strings", strBuilder.build());
+        pojoBuilder.add("bigDecimals", bigDecBuilder.build());
+        pojoBuilder.add("booleans", blnBuilder.build());
+
+        JsonObject jsonObject = pojoBuilder.build();
+        Pojo pojo = yassonJsonb.fromJsonStructure(jsonObject, Pojo.class);
+
+        assertEquals(1, pojo.getBigDecimals().size());
+        assertEquals(1, pojo.getStrings().size());
+        assertEquals(1, pojo.getBooleans().size());
+    }
+
+    @Test
+    public void testNestedArrays() {
+        JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder();
+        JsonArrayBuilder innerArrBuilder = jsonProvider.createArrayBuilder();
+        innerArrBuilder.add("first").add("second");
+        arrayBuilder.add(BigDecimal.TEN);
+        arrayBuilder.add(innerArrBuilder.build());
+
+        JsonArray jsonArray = arrayBuilder.build();
+
+        ArrayList result = yassonJsonb.fromJsonStructure(jsonArray, ArrayList.class);
+        assertEquals(2, result.size());
+        assertEquals(BigDecimal.TEN, result.get(0));
+        assertTrue(result.get(1) instanceof List);
+        List inner = (List) result.get(1);
+        assertEquals(2, inner.size());
+        assertEquals("first", inner.get(0));
+        assertEquals("second", inner.get(1));
+    }
+
+    @Test
+    public void testObjectsNestedInArrays() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+
+        JsonArrayBuilder innerArrayBuilder = jsonProvider.createArrayBuilder();
+        innerArrayBuilder.add("String value 1");
+        objectBuilder.add("strings", innerArrayBuilder.build());
+
+        JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder();
+        arrayBuilder.add(objectBuilder.build());
+
+        JsonArray rootArray = arrayBuilder.build();
+
+        List<Object> result = yassonJsonb.fromJsonStructure(rootArray, new TestTypeToken<List<Pojo>>(){}.getType());
+        assertTrue(result.get(0) instanceof Pojo);
+        Pojo pojo = (Pojo) result.get(0);
+        assertNotNull(pojo);
+        assertEquals("value 1", pojo.getStringProperty());
+        assertEquals(new BigDecimal("1.1"), pojo.getBigDecimalProperty());
+        assertEquals(Long.valueOf(10), pojo.getLongProperty());
+        assertNotNull(pojo.getStrings());
+        assertEquals(1, pojo.getStrings().size());
+        assertEquals("String value 1", pojo.getStrings().get(0));
+    }
+
+    @Test
+    public void testObjectsNestedInArraysRaw() {
+        JsonObjectBuilder objectBuilder = jsonProvider.createObjectBuilder();
+        objectBuilder.add("stringProperty", "value 1");
+        objectBuilder.add("bigDecimalProperty", new BigDecimal("1.1"));
+        objectBuilder.add("longProperty", 10L);
+
+        JsonArrayBuilder innerArrayBuilder = jsonProvider.createArrayBuilder();
+        innerArrayBuilder.add("String value 1");
+
+        objectBuilder.add("strings", innerArrayBuilder.build());
+
+        JsonArrayBuilder arrayBuilder = jsonProvider.createArrayBuilder();
+        arrayBuilder.add(10L);
+        arrayBuilder.add(objectBuilder.build());
+        arrayBuilder.add("10");
+
+        JsonArray rootArray = arrayBuilder.build();
+
+        List<Object> result = yassonJsonb.fromJsonStructure(rootArray, new TestTypeToken<List<Object>>(){}.getType());
+        assertEquals(new BigDecimal("10"), result.get(0));
+        assertTrue(result.get(1) instanceof Map);
+        Map pojo = (Map) result.get(1);
+        assertNotNull(pojo);
+        assertEquals("value 1", pojo.get("stringProperty"));
+        assertEquals(new BigDecimal("1.1"), pojo.get("bigDecimalProperty"));
+        assertEquals(new BigDecimal(10), pojo.get("longProperty"));
+        assertTrue(pojo.get("strings") instanceof List);
+        List strings = (List) pojo.get("strings");
+        assertNotNull(strings);
+        assertEquals(1, strings.size());
+        assertEquals("String value 1", strings.get(0));
+    }
+
+
+    @Test
+    public void testCustomJsonbDeserializer() {
+        JsonObjectBuilder outerBuilder = jsonProvider.createObjectBuilder();
+        JsonObjectBuilder innerBuilder = jsonProvider.createObjectBuilder();
+        innerBuilder.add("first", "String value 1");
+        innerBuilder.add("second", "String value 2");
+        outerBuilder.add("inner", innerBuilder.build());
+        JsonObject object = outerBuilder.build();
+
+        YassonJsonb jsonb = (YassonJsonb) JsonbBuilder.create(new JsonbConfig().withDeserializers(new InnerPojoDeserializer()));
+        Pojo result = jsonb.fromJsonStructure(object, Pojo.class);
+        assertNotNull(result.getInner());
+        assertEquals("String value 1", result.getInner().getInnerFirst());
+        assertEquals("String value 2", result.getInner().getInnerSecond());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/jsonstructure/Pojo.java b/src/test/java/org/eclipse/yasson/jsonstructure/Pojo.java
new file mode 100644
index 0000000..ead596c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/jsonstructure/Pojo.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.jsonstructure;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+public final class Pojo {
+
+    private String stringProperty;
+    private InnerPojo inner;
+    private BigDecimal bigDecimalProperty;
+    private Long longProperty;
+
+    private List<String> strings;
+    private List<BigDecimal> bigDecimals;
+    private List<Boolean> booleans;
+
+    public String getStringProperty() {
+        return stringProperty;
+    }
+
+    public void setStringProperty(String stringProperty) {
+        this.stringProperty = stringProperty;
+    }
+
+    public BigDecimal getBigDecimalProperty() {
+        return bigDecimalProperty;
+    }
+
+    public void setBigDecimalProperty(BigDecimal bigDecimalProperty) {
+        this.bigDecimalProperty = bigDecimalProperty;
+    }
+
+    public InnerPojo getInner() {
+        return inner;
+    }
+
+    public void setInner(InnerPojo inner) {
+        this.inner = inner;
+    }
+
+    public Long getLongProperty() {
+        return longProperty;
+    }
+
+    public void setLongProperty(Long longProperty) {
+        this.longProperty = longProperty;
+    }
+
+    public List<String> getStrings() {
+        return strings;
+    }
+
+    public void setStrings(List<String> strings) {
+        this.strings = strings;
+    }
+
+    public List<BigDecimal> getBigDecimals() {
+        return bigDecimals;
+    }
+
+    public void setBigDecimals(List<BigDecimal> bigDecimals) {
+        this.bigDecimals = bigDecimals;
+    }
+
+    public List<Boolean> getBooleans() {
+        return booleans;
+    }
+
+    public void setBooleans(List<Boolean> booleans) {
+        this.booleans = booleans;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/logger/JsonbLoggerFormatter.java b/src/test/java/org/eclipse/yasson/logger/JsonbLoggerFormatter.java
new file mode 100644
index 0000000..731e805
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/logger/JsonbLoggerFormatter.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.logger;
+
+import java.util.logging.LogRecord;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class JsonbLoggerFormatter extends java.util.logging.Formatter {
+    @Override
+    public String format(LogRecord record) {
+        return String.format("%-8s",record.getLevel()) + " =>  " + record.getMessage()+"\n";
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/records/Car.java b/src/test/java/org/eclipse/yasson/records/Car.java
new file mode 100644
index 0000000..d32e317
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/Car.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2021, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import jakarta.json.bind.annotation.JsonbProperty;
+
+public record Car(@JsonbProperty("typeChanged") String type, @JsonbProperty("colorChanged") String color) {
+}
diff --git a/src/test/java/org/eclipse/yasson/records/CarWithCreateNamingStrategyTest.java b/src/test/java/org/eclipse/yasson/records/CarWithCreateNamingStrategyTest.java
new file mode 100644
index 0000000..d1875be
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/CarWithCreateNamingStrategyTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2022, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.eclipse.yasson.records;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.config.PropertyNamingStrategy;
+import org.junit.jupiter.api.Test;
+
+import static java.util.Objects.requireNonNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CarWithCreateNamingStrategyTest {
+
+    // camel case is intentional for this test case
+    public record Car(String brandName, String colorName) {
+
+        @JsonbCreator
+        public Car {
+            requireNonNull(brandName, "brandName");
+            requireNonNull(colorName, "colorName");
+        }
+    }
+
+    @Test
+    public void testRecordJsonbCreatorWithNamingStrategy() {
+        // given
+        final JsonbConfig config = new JsonbConfig()
+                .withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_UNDERSCORES);
+        final Jsonb jsonb = JsonbBuilder.create(config);
+
+        var json = """
+                {
+                  "brand_name": "Volkswagen",
+                  "color_name": "Piano black"
+                }
+                """;
+
+        // when
+        final Car car = jsonb.fromJson(json, Car.class);
+
+        // then
+        assertEquals("Volkswagen", car.brandName());
+        assertEquals("Piano black", car.colorName());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/records/CarWithCreator.java b/src/test/java/org/eclipse/yasson/records/CarWithCreator.java
new file mode 100644
index 0000000..b19fa0b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/CarWithCreator.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+public record CarWithCreator(String type, String color) {
+
+    @JsonbCreator
+    public static CarWithCreator create(@JsonbProperty("type") String type, @JsonbProperty("color") String color) {
+        return new CarWithCreator(type, color);
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/records/CarWithDefaultConstructor.java b/src/test/java/org/eclipse/yasson/records/CarWithDefaultConstructor.java
new file mode 100644
index 0000000..c5bc540
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/CarWithDefaultConstructor.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2022, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+public record CarWithDefaultConstructor(String type, String color) {
+
+    public CarWithDefaultConstructor() {
+        this("some", "other");
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/records/CarWithExtraMethod.java b/src/test/java/org/eclipse/yasson/records/CarWithExtraMethod.java
new file mode 100644
index 0000000..35cba7b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/CarWithExtraMethod.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2022, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+public record CarWithExtraMethod(String type, String color) {
+
+    public String type() {
+        return type;
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/records/CarWithMultipleConstructors.java b/src/test/java/org/eclipse/yasson/records/CarWithMultipleConstructors.java
new file mode 100644
index 0000000..59e302d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/CarWithMultipleConstructors.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2022, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+public record CarWithMultipleConstructors(String type, String color) {
+
+    public CarWithMultipleConstructors(String type) {
+        this(type, "red");
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/records/CarWithMultipleConstructorsAndCreator.java b/src/test/java/org/eclipse/yasson/records/CarWithMultipleConstructorsAndCreator.java
new file mode 100644
index 0000000..c98fd21
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/CarWithMultipleConstructorsAndCreator.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import jakarta.json.bind.annotation.JsonbCreator;
+import jakarta.json.bind.annotation.JsonbProperty;
+
+public record CarWithMultipleConstructorsAndCreator(String type, String color) {
+
+    @JsonbCreator
+    public CarWithMultipleConstructorsAndCreator(@JsonbProperty("type") String type) {
+        this(type, "red");
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/records/CarWithoutAnnotations.java b/src/test/java/org/eclipse/yasson/records/CarWithoutAnnotations.java
new file mode 100644
index 0000000..b6cca66
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/CarWithoutAnnotations.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2021, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+public record CarWithoutAnnotations(String type, String color) {
+}
diff --git a/src/test/java/org/eclipse/yasson/records/RecordTest.java b/src/test/java/org/eclipse/yasson/records/RecordTest.java
new file mode 100644
index 0000000..2b6a61e
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/records/RecordTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2021, 2024 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.records;
+
+import jakarta.json.bind.JsonbException;
+
+import org.eclipse.yasson.Jsonbs;
+import org.eclipse.yasson.internal.properties.MessageKeys;
+import org.eclipse.yasson.internal.properties.Messages;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class RecordTest {
+
+    @Test
+    public void testRecordProcessing() {
+        Car car = new Car("skoda", "green");
+        String expected = "{\"colorChanged\":\"green\",\"typeChanged\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertEquals(expected, json);
+        Car deserialized = Jsonbs.defaultJsonb.fromJson(expected, Car.class);
+        assertEquals(car, deserialized);
+    }
+
+    @Test
+    public void testRecordProcessingWithoutJsonbProperties() {
+        CarWithoutAnnotations car = new CarWithoutAnnotations("skoda", "green");
+        String expected = "{\"color\":\"green\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertEquals(expected, json);
+        CarWithoutAnnotations deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithoutAnnotations.class);
+        assertEquals(car, deserialized);
+    }
+
+    @Test
+    public void testRecordProcessingWithExtraMethod() {
+        CarWithExtraMethod car = new CarWithExtraMethod("skoda", "green");
+        String expected = "{\"color\":\"green\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertThat(json, is(expected));
+        CarWithExtraMethod deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithExtraMethod.class);
+        assertThat(deserialized, is(car));
+    }
+
+    @Test
+    public void testRecordMultipleConstructors() {
+        CarWithMultipleConstructors car = new CarWithMultipleConstructors("skoda");
+        String expected = "{\"color\":\"red\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertThat(json, is(expected));
+        JsonbException jsonbException = assertThrows(JsonbException.class,
+                                                     () -> Jsonbs.defaultJsonb.fromJson(expected,
+                                                                                        CarWithMultipleConstructors.class));
+        String expectedMessage = Messages.getMessage(MessageKeys.RECORD_MULTIPLE_CONSTRUCTORS, CarWithMultipleConstructors.class);
+        assertThat(jsonbException.getMessage(), is(expectedMessage));
+    }
+
+    @Test
+    public void testRecordMultipleConstructorsWithJsonbCreator() {
+        CarWithMultipleConstructorsAndCreator car = new CarWithMultipleConstructorsAndCreator("skoda");
+        String expected = "{\"color\":\"red\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertThat(json, is(expected));
+        CarWithMultipleConstructorsAndCreator deserialized =  Jsonbs.defaultJsonb
+                .fromJson(expected, CarWithMultipleConstructorsAndCreator.class);
+        assertThat(car, is(deserialized));
+    }
+
+    @Test
+    public void testRecordJsonbCreator() {
+        CarWithCreator car = new CarWithCreator("skoda", "red");
+        String expected = "{\"color\":\"red\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertThat(json, is(expected));
+        CarWithCreator deserialized = Jsonbs.defaultJsonb.fromJson(expected, CarWithCreator.class);
+        assertThat(deserialized, is(car));
+    }
+
+    @Test
+    public void testRecordWithDefaultConstructor() {
+        CarWithDefaultConstructor car = new CarWithDefaultConstructor("skoda", "red");
+        String expected = "{\"color\":\"red\",\"type\":\"skoda\"}";
+
+        String json = Jsonbs.defaultJsonb.toJson(car);
+        assertThat(json, is(expected));
+        assertThrows(JsonbException.class, () -> Jsonbs.defaultJsonb.fromJson(expected, CarWithDefaultConstructor.class));
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/MapToEntriesArraySerializerTest.java b/src/test/java/org/eclipse/yasson/serializers/MapToEntriesArraySerializerTest.java
new file mode 100644
index 0000000..3961dd2
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/MapToEntriesArraySerializerTest.java
@@ -0,0 +1,937 @@
+/*
+ * Copyright (c) 2019, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers;
+
+import org.junit.jupiter.api.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.StringReader;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import jakarta.json.Json;
+import jakarta.json.JsonArray;
+import jakarta.json.JsonObject;
+import jakarta.json.JsonValue;
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.serializers.model.Pokemon;
+import org.eclipse.yasson.serializers.model.Trainer;
+
+/**
+ * Test various use-cases with {@code Map} serializer and de-serializer which
+ * stores Map.Entries as JSON objects in 1:1 relation.
+ */
+public class MapToEntriesArraySerializerTest {
+
+    /**
+     * Comparator that allows to compare at least some similar Number instances.
+     * Does not work for everything, but it's sufficient for used test cases.
+     */
+    private static final class NumberComparator implements Comparator<Number> {
+
+        @Override
+        public int compare(Number n1, Number n2) {
+            if (n1 != null && n2 != null) {
+                if (((n1 instanceof Float) || (n1 instanceof Double))
+                    && ((n2 instanceof Float) || (n2 instanceof Double))) {
+                    return Double.compare(n1.doubleValue(), n2.doubleValue());
+                }
+                return Long.compare(n1.longValue(), n2.longValue());
+            }
+            if (n1 == null) {
+                return n2 == null ? 0 : -1;
+            } else {
+                return 1;
+            }
+        }
+    }
+
+    /** NumberComparator instance to be used. */
+    private static final Comparator<Number> CMP_NUM = new NumberComparator();
+
+    // Verification code for serialization: Covers only use-cases used in jUnit tests.
+    // * Key JsonObject is always mapped to Trainer PoJo.
+    // * Value JsonObject is always mapped to Pokemon Pojo.
+
+    /**
+     * Verify that source Map value and parsed Map entry JsonObject value are equals.
+     *
+     * @param jentry parsed Map entry as JsonObject
+     * @param source source Map for value verification
+     * @param key Map key used to retrieve value
+     */
+    private static final <K,V> void verifyMapValues(JsonObject jentry, Map<K,V> source, K key) {
+        assertNotNull(jentry);
+        assertNotNull(source);
+        assertNotNull(key);
+        V sourceValue = source.get(key);
+        assertNotNull(sourceValue);
+        switch (jentry.getValue("/value").getValueType()) {
+            // Value contains JSON object: it shall be Pokemon PoJo
+            case OBJECT:
+                JsonObject valueObject = jentry.getJsonObject("value");
+                Pokemon sourcePoJo = (Pokemon) source.get(key);
+                Pokemon valuePojo = new Pokemon(
+                        valueObject.getString("name"), valueObject.getString("type"), valueObject.getInt("cp"));
+                assertEquals(sourcePoJo, valuePojo);
+                break;
+            case STRING:
+                String valueString = jentry.getString("value");
+                String sourceString = (String) source.get(key);
+                assertEquals(sourceString, valueString);
+                break;
+            case NUMBER:
+                Number valueNumber = jentry.getJsonNumber("value").numberValue();
+                Number sourceNumber = (Number) source.get(key);
+                // Number comparator shall be used here because values may not be of the same type.
+                assertEquals(0, CMP_NUM.compare(sourceNumber, valueNumber));
+                break;
+            case TRUE:
+            case FALSE:
+                Boolean valueBool = jentry.getBoolean("value");
+                Boolean sourceBool = (Boolean) source.get(key);
+                assertEquals(sourceBool, valueBool);
+                break;
+            default:
+                throw new IllegalStateException(jentry.getValue("/value").getValueType() + "was not expected");
+        }
+    }
+
+    /**
+     * Verify that source Map value and parsed Map entry JsonObject value are equals.
+     *
+     * @param jentry parsed Map entry as JsonObject
+     * @param sourceEntry source Map entry for value verification
+     */
+    private static final <K,V> void verifyMapValues(JsonObject jentry, Map.Entry<K[],V> sourceEntry) {
+        assertNotNull(jentry);
+        assertNotNull(sourceEntry);
+        switch (jentry.getValue("/value").getValueType()) {
+            // Value contains JSON object: it shall be Pokemon PoJo
+            case OBJECT:
+                JsonObject valueObject = jentry.getJsonObject("value");
+                Pokemon sourcePoJo = (Pokemon) sourceEntry.getValue();
+                Pokemon valuePojo = new Pokemon(
+                        valueObject.getString("name"), valueObject.getString("type"), valueObject.getInt("cp"));
+                assertEquals(sourcePoJo, valuePojo);
+                break;
+            case ARRAY:
+                JsonArray valueArray = jentry.getJsonArray("value");
+                assertTrue(valueArray.size() > 0);
+                verifyMapArrayValue(jentry, valueArray, sourceEntry);
+                break;
+            case STRING:
+                String valueString = jentry.getString("value");
+                String sourceString = (String) sourceEntry.getValue();
+                assertEquals(sourceString, valueString);
+                break;
+            case NUMBER:
+                Number valueNumber = jentry.getJsonNumber("value").numberValue();
+                Number sourceNumber = (Number) sourceEntry.getValue();
+                // Number comparator shall be used here because values may not be of the same type.
+                assertEquals(0, CMP_NUM.compare(sourceNumber, valueNumber));
+                break;
+            case TRUE:
+            case FALSE:
+                Boolean valueBool = jentry.getBoolean("value");
+                Boolean sourceBool = (Boolean) sourceEntry.getValue();
+                assertEquals(sourceBool, valueBool);
+                break;
+            default:
+                throw new IllegalStateException(jentry.getValue("/value").getValueType() + "was not expected");
+        }
+    }
+
+    /**
+     * Retrieve Map.Entry with matching array key from source Map.
+     *
+     * @param source source Map
+     * @param key array key to search for
+     * @param cmp optional comparator to use for search
+     * @param keys source map key Set used to check whether all keys were processed. Key will be removed from set on successful match
+     * @return Map.Entry matching provided key
+     */
+    private static final <K,V> Map.Entry<K[], V> getMapEntryForArrayKey(Map<K[], V> source, K[] key, Comparator<K> cmp, Set<K> keys) {
+        for (Map.Entry<K[], V> entry : source.entrySet()) {
+            K[] sourceKey = entry.getKey();
+            boolean match = key.length == sourceKey.length;
+            if (match) {
+                for (int i = 0; i < key.length && match; i++) {
+                    if (cmp != null) {
+                        match = (cmp.compare(key[i], sourceKey[i]) == 0);
+                    } else {
+                        match = key[i].equals(sourceKey[i]);
+                    }
+                }
+                // Matching key is removed from Set for key processing check
+                if (match) {
+                    keys.remove(entry.getKey());
+                    return entry;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Verify that source Map array value and parsed map array value are the same.
+     *
+     * @param value parsed value
+     * @param sourceValue source Map value
+     * @param cmp optional comparator to use for verification
+     */
+    private static final <V> void verifyMapArrayValues(V[] value, V[] sourceValue, Comparator<V> cmp) {
+        assertEquals(sourceValue.length, value.length);
+        for (int i = 0; i < sourceValue.length; i++) {
+            if (cmp != null) {
+                assertTrue(cmp.compare(sourceValue[i], value[i]) == 0);
+            } else {
+                assertEquals(sourceValue[i], value[i]);
+            }
+        }
+    }
+
+    /**
+     * Build Map key as an array. Get corresponding key from source Map.
+     *
+     * @param jentry Map key parsed as JsonArray
+     * @param sourceEntry source Map
+     */
+    @SuppressWarnings("unchecked")
+    private static final <K,V> void verifyMapArrayValue(JsonObject jentry, final JsonArray valueArray, Map.Entry<K[],V> sourceEntry) {
+        int size = valueArray.size();
+        // All array elements in the tests are of the same type.
+        switch (valueArray.get(0).getValueType()) {
+            case OBJECT:
+                V[] keyPoJo = (V[]) new Pokemon[size];
+                for (int i = 0; i < size; i++) {
+                    JsonObject keyItem = valueArray.getJsonObject(i);
+                    keyPoJo[i] = (V) new Pokemon(keyItem.getString("name"), keyItem.getString("type"), keyItem.getInt("cp"));
+                }
+                verifyMapArrayValues(keyPoJo, (V[]) sourceEntry.getValue(), null);
+                break;
+            case STRING:
+                V[] keyString = (V[]) new String[size];
+                for (int i = 0; i < size; i++) {
+                    keyString[i] = (V) valueArray.getString(i);
+                }
+                verifyMapArrayValues(keyString, (V[]) sourceEntry.getValue(), null);
+                break;
+            case NUMBER:
+                V[] keyNumber = (V[]) new Number[size];
+                for (int i = 0; i < size; i++) {
+                    keyNumber[i] = (V) valueArray.getJsonNumber(i).numberValue();
+                }
+                verifyMapArrayValues(keyNumber, (V[]) sourceEntry.getValue(), (Comparator<V>) CMP_NUM);
+                break;
+            case TRUE:
+            case FALSE:
+                V[] keyBool = (V[]) new Boolean[size];
+                for (int i = 0; i < size; i++) {
+                    keyBool[i] = (V) Boolean.valueOf(valueArray.getBoolean(i));
+                }
+                verifyMapArrayValues(keyBool, (V[]) sourceEntry.getValue(), null);
+                break;
+            default:
+                throw new IllegalStateException(valueArray.getValueType() + "was not expected");
+        }
+    }
+
+    /**
+     * Build Map key as an array. Get corresponding key from source Map.
+     *
+     * @param keyArray Map key parsed as JsonArray
+     * @param source source Map
+     */
+    @SuppressWarnings("unchecked")
+    private static final <K,V> void verifyMapArrayKey(JsonObject jentry, final JsonArray keyArray, Map<K,V> source, Set<K> keys) {
+        int size = keyArray.size();
+        // All array elements in the tests are of the same type.
+        switch (keyArray.get(0).getValueType()) {
+            case OBJECT:
+                K[] keyPoJo = (K[]) new Trainer[size];
+                for (int i = 0; i < size; i++) {
+                    JsonObject keyItem = keyArray.getJsonObject(i);
+                    keyPoJo[i] = (K) new Trainer(keyItem.getString("name"), keyItem.getInt("age"));
+                }
+                Map.Entry<K[],V> entryObject = getMapEntryForArrayKey((Map<K[],V>) source, keyPoJo, null, keys);
+                verifyMapValues(jentry, entryObject);
+                break;
+            case STRING:
+                K[] keyString = (K[]) new String[size];
+                for (int i = 0; i < size; i++) {
+                    keyString[i] = (K) keyArray.getString(i);
+                }
+                Map.Entry<K[],V> entryString = getMapEntryForArrayKey((Map<K[],V>) source, keyString, null, keys);
+                verifyMapValues(jentry, entryString);
+                break;
+            case NUMBER:
+                K[] keyNumber = (K[]) new Number[size];
+                for (int i = 0; i < size; i++) {
+                    keyNumber[i] = (K) keyArray.getJsonNumber(i).numberValue();
+                }
+                Map.Entry<K[],V> entryNumber = getMapEntryForArrayKey((Map<K[],V>) source, keyNumber, (Comparator<K>) CMP_NUM, keys);
+                verifyMapValues(jentry, entryNumber);
+                break;
+            case TRUE:
+            case FALSE:
+                K[] keyBool = (K[]) new Boolean[size];
+                for (int i = 0; i < size; i++) {
+                    keyBool[i] = (K) Boolean.valueOf(keyArray.getBoolean(i));
+                }
+                Map.Entry<K[],V> entryBool = getMapEntryForArrayKey((Map<K[],V>) source, keyBool, null, keys);
+                verifyMapValues(jentry, entryBool);
+                break;
+            default:
+                throw new IllegalStateException(keyArray.getValueType() + "was not expected");
+        }
+    }
+
+    /**
+     * Verify that serialized Map provided as JsonArray matches source Map.
+     *
+     * @param source source Map
+     * @param array  serialized Map parsed and provided as JsonArray
+     */
+    @SuppressWarnings("unchecked")
+    private static final <K,V> void verifySerialization(Map<K, V> source, JsonArray array) {
+        assertEquals(source.size(), array.size());
+        Set<K> keys = source.keySet();
+        array.forEach(entry -> {
+            JsonObject jentry = entry.asJsonObject();
+            //JsonValue key = jentry.getValue("/key");
+            switch (jentry.getValue("/key").getValueType()) {
+                // Key contains JSON object: it shall be Trainer PoJo
+                case OBJECT: {
+                    JsonObject keyValue = jentry.getJsonObject("key");
+                    Trainer keyPoJo = new Trainer(keyValue.getString("name"), keyValue.getInt("age"));
+                    verifyMapValues(jentry, source, (K) keyPoJo);
+                    keys.remove((K) keyPoJo);
+                }
+                    break;
+                case ARRAY: {
+                    JsonArray keyArray = jentry.getJsonArray("key");
+                    assertTrue(keyArray.size() > 0);
+                    verifyMapArrayKey(jentry, keyArray, source, keys);
+                }
+                    break;
+                case STRING: {
+                    String keyValue = jentry.getString("key");
+                    verifyMapValues(jentry, source, (K) keyValue);
+                    keys.remove((K) keyValue);
+                }
+                    break;
+                case NUMBER: {
+                    Number keyValue = jentry.getJsonNumber("key").numberValue();
+                    verifyMapValues(jentry, source, (K) keyValue);
+                    keys.remove((K) keyValue);
+                }
+                    break;
+                case TRUE:
+                case FALSE: {
+                    Boolean keyValue = jentry.getBoolean("key");
+                    verifyMapValues(jentry, source, (K) keyValue);
+                    keys.remove((K) keyValue);
+                }
+                    break;
+                default:
+                    throw new IllegalStateException(jentry.getValue("/value").getValueType() + "was not expected");
+            }
+        });
+        // Verify that all keys were processed.
+        assertTrue(keys.isEmpty());
+    }
+
+//No longer valid test
+//    /**
+//     * Test serialization of Map with Number keys and String values.
+//     */
+//    @Test
+//    public void testSerializeNumberStringMapToEntriesArray() {
+//        Map<Number, String> map = new TreeMap<>(CMP_NUM);
+//        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
+//        map.put(Integer.valueOf(12), "twelve");
+//        map.put(Short.valueOf((short)48), "forty eight");
+//        map.put(Long.valueOf(256), "two hundred fifty-six");
+//        String json = jsonb.toJson(map);
+//        JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray();
+//        verifySerialization(map, jarr);
+//    }
+
+    /**
+     * Test serialization of Map with PoJo keys and PoJo values.
+     */
+    @Test
+    public void testSerializePoJoPoJoMapToEntriesArray() {
+        Map<Trainer, Pokemon> map = new HashMap<>();
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
+        map.put(new Trainer("John Smith", 35), new Pokemon("Charmander", "fire", 980));
+        map.put(new Trainer("Tom Jones", 24), new Pokemon("Caterpie", "bug", 437));
+        map.put(new Trainer("Peter Wright", 27), new Pokemon("Houndour", "dark", 1234));
+        map.put(new Trainer("Bob Parker", 19), new Pokemon("Sneasel", "ice", 2051));
+        String json = jsonb.toJson(map);
+        JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray();
+        verifySerialization(map, jarr);
+    }
+
+    /**
+     * Test serialization of Map with mixed simple keys and mixed simple values.
+     */
+    @Test
+    public void testSerializeSimpleSimpleMapToEntriesArray() {
+        String expected = "{\"false\":true,\"10\":24,\"Name\":\"John Smith\"}";
+        Map<Object, Object> map = new HashMap<>();
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+        map.put("Name", "John Smith");
+        map.put(Integer.valueOf(10), Long.valueOf(24l));
+        map.put(Boolean.FALSE, Boolean.TRUE);
+        String json = jsonb.toJson(map);
+        assertEquals(expected, json);
+    }
+
+    /**
+     * Test serialization of Map with arrays of simple values.
+     */
+    @Test
+    public void testSerializeSimpleArraySimpleArrayMapToEntriesArray() {
+        Map<Object, Object> map = new HashMap<>();
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
+        map.put(new String[] {"John", "Smith"}, new String[] {"first name", "second name"});
+        map.put(new String[] {"Pikachu", "electric"}, new String[] {"pokemon name", "pokemon type"});
+        map.put(
+                new Trainer[] {new Trainer("Bob", 15), new Trainer("Ash", 12)},
+                new Pokemon[] {new Pokemon("Charmander", "fire", 1245), new Pokemon("Kyogre", "water", 3056)});
+        String json = jsonb.toJson(map);
+        JsonArray jarr = Json.createReader(new StringReader(json)).read().asJsonArray();
+        verifySerialization(map, jarr);
+    }
+
+    /**
+     * Test de-serialization of Map<?, ?> with various simple values as keys and
+     * values. Map is stored as an JsonArray of map entries represented as
+     * JsonObjects.
+     */
+    @Test
+    public void testDeSerializePrimitivesMapToEntriesArray() {
+        String jsonString = "[" +
+            "    {" +
+            "        \"key\": \"first\"," +
+            "        \"value\": \"Peter Parker\""  +
+            "    }," +
+            "    {" +
+            "        \"key\": 42," +
+            "        \"value\": true" +
+            "    }," +
+            "    {" +
+            "        \"key\": false," +
+            "        \"value\": 21" +
+            "    }" +
+            "]";
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+        Map<?, ?> map = jsonb.fromJson(jsonString, Map.class);
+        assertEquals(3, map.size());
+        // Make sure that all 3 pokemons were checked.
+        int valueCheck = 0x00;
+        for (Map.Entry<?, ?> entry : map.entrySet()) {
+            if ((entry.getKey() instanceof String) && "first".equals(entry.getKey())) {
+                assertEquals("Peter Parker", entry.getValue());
+                valueCheck |= 0x01;
+            }
+            if ((entry.getKey() instanceof Number) && entry.getKey().equals(new BigDecimal(42))) {
+                assertEquals(true, entry.getValue());
+                valueCheck |= 0x02;
+            }
+            if ((entry.getKey() instanceof Boolean) && entry.getKey().equals(false)) {
+                assertEquals(new BigDecimal(21), entry.getValue());
+                valueCheck |= 0x04;
+            }
+        }
+        if ((valueCheck & 0x01) == 0) {
+            fail("Did not find key \"first\" in the Map");
+        }
+        if ((valueCheck & 0x02) == 0) {
+            fail("Did not find key 42 in the Map");
+        }
+        if ((valueCheck & 0x04) == 0) {
+            fail("Did not find key false in the Map");
+        }
+    }
+
+    /**
+     * Test de-serialization of Map<String, Pokemon> with various classes instances
+     * as keys. Map is stored as an JsonArray of map entries represented as
+     * JsonObjects.
+     */
+    @Test
+    public void testDeSerializeStringPoJoMapToEntriesArray() {
+        String jsonString = "[" +
+            "    {" +
+            "        \"key\": \"Pikachu\"," +
+            "        \"value\": {" +
+            "            \"name\": \"Pikachu\"," +
+            "            \"type\": \"electric\"," +
+            "            \"cp\": 456" + "         }" +
+            "    }," +
+            "    {" +
+            "        \"key\": \"Squirtle\"," +
+            "        \"value\": {" +
+            "            \"name\": \"Squirtle\"," +
+            "            \"type\": \"water\"," +
+            "            \"cp\": 124" +
+            "         }" +
+            "    }," +
+            "    {" +
+            "        \"key\": \"Rayquaza\"," +
+            "        \"value\": {" +
+            "            \"name\": \"Rayquaza\"," +
+            "            \"type\": \"dragon\"," +
+            "            \"cp\": 3273" +
+            "        }" +
+            "    }" +
+            "]";
+        ParameterizedType pt = new ParameterizedType() {
+            @Override
+            public Type[] getActualTypeArguments() {
+                return new Type[] { String.class, Pokemon.class };
+            }
+
+            @Override
+            public Type getRawType() {
+                return Map.class;
+            }
+
+            @Override
+            public Type getOwnerType() {
+                return null;
+            }
+        };
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+        Map<String, Pokemon> map = jsonb.fromJson(jsonString, pt);
+        assertEquals(3, map.size());
+        // Make sure that all 3 pokemons were checked.
+        int valueCheck = 0x00;
+        for (Map.Entry<String, Pokemon> entry : map.entrySet()) {
+            Pokemon pokemon = entry.getValue();
+            if ("Pikachu".equals(entry.getKey())) {
+                assertEquals("Pikachu", pokemon.name);
+                assertEquals("electric", pokemon.type);
+                assertEquals(456, pokemon.cp);
+                valueCheck |= 0x01;
+            }
+            if ("Squirtle".equals(entry.getKey())) {
+                assertEquals("Squirtle", pokemon.name);
+                assertEquals("water", pokemon.type);
+                assertEquals(124, pokemon.cp);
+                valueCheck |= 0x02;
+            }
+            if ("Rayquaza".equals(entry.getKey())) {
+                assertEquals("Rayquaza", pokemon.name);
+                assertEquals("dragon", pokemon.type);
+                assertEquals(3273, pokemon.cp);
+                valueCheck |= 0x04;
+            }
+        }
+        if ((valueCheck & 0x01) == 0) {
+            fail("Did not find key \"Pikachu\" in the Map");
+        }
+        if ((valueCheck & 0x02) == 0) {
+            fail("Did not find key \"Squirtle\" in the Map");
+        }
+        if ((valueCheck & 0x04) == 0) {
+            fail("Did not find key \"Rayquaza\" in the Map");
+        }
+    }
+
+    /**
+     * Test de-serialization of Map<Trainer, Pokemon> with various classes instances
+     * as keys. Map is stored as an JsonArray of map entries represented as
+     * JsonObjects.
+     */
+    @Test
+    public void testDeSerializePoJoPoJoMapToEntriesArray() {
+        String jsonString = "[" +
+            "    {" +
+            "        \"key\": {" +
+            "            \"name\": \"Bob\"," +
+            "            \"age\": 12" + "        }," +
+            "        \"value\": {" +
+            "            \"name\": \"Pikachu\"," +
+            "            \"type\": \"electric\"," +
+            "            \"cp\": 456" + "         }" +
+            "    }," +
+            "    {" +
+            "        \"key\": {" +
+            "            \"name\": \"Ash\"," +
+            "            \"age\": 10" +
+            "        }," +
+            "        \"value\": {" +
+            "            \"name\": \"Squirtle\"," +
+            "            \"type\": \"water\"," +
+            "            \"cp\": 124" +
+            "         }" +
+            "    }," +
+            "    {" +
+            "        \"key\": {" +
+            "            \"name\": \"Joe\"," +
+            "            \"age\": 15" +
+            "        }," +
+            "        \"value\": {" +
+            "            \"name\": \"Rayquaza\"," +
+            "            \"type\": \"dragon\"," +
+            "            \"cp\": 3273" +
+            "        }" +
+            "    }" +
+            "]";
+        ParameterizedType pt = new ParameterizedType() {
+            @Override
+            public Type[] getActualTypeArguments() {
+                return new Type[] { Trainer.class, Pokemon.class };
+            }
+
+            @Override
+            public Type getRawType() {
+                return Map.class;
+            }
+
+            @Override
+            public Type getOwnerType() {
+                return null;
+            }
+        };
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+        Map<Trainer, Pokemon> map = jsonb.fromJson(jsonString, pt);
+        assertEquals(3, map.size());
+        // Make sure that all 3 pokemons were checked.
+        int valueCheck = 0x00;
+        for (Map.Entry<Trainer, Pokemon> entry : map.entrySet()) {
+            Trainer trainer = entry.getKey();
+            Pokemon pokemon = entry.getValue();
+            if ("Bob".equals(trainer.name)) {
+                assertEquals(12, trainer.age);
+                assertEquals("Pikachu", pokemon.name);
+                assertEquals("electric", pokemon.type);
+                assertEquals(456, pokemon.cp);
+                valueCheck |= 0x01;
+            }
+            if ("Ash".equals(trainer.name)) {
+                assertEquals(10, trainer.age);
+                assertEquals("Squirtle", pokemon.name);
+                assertEquals("water", pokemon.type);
+                assertEquals(124, pokemon.cp);
+                valueCheck |= 0x02;
+            }
+            if ("Joe".equals(trainer.name)) {
+                assertEquals(15, trainer.age);
+                assertEquals("Rayquaza", pokemon.name);
+                assertEquals("dragon", pokemon.type);
+                assertEquals(3273, pokemon.cp);
+                valueCheck |= 0x04;
+            }
+        }
+        if ((valueCheck & 0x01) == 0) {
+            fail("Did not find key \"Bob\" in the Map");
+        }
+        if ((valueCheck & 0x02) == 0) {
+            fail("Did not find key \"Ash\" in the Map");
+        }
+        if ((valueCheck & 0x04) == 0) {
+            fail("Did not find key \"Joe\" in the Map");
+        }
+    }
+
+    /**
+     * Test de-serialization of Map<Integer[], String[]>.
+     * Map is stored as an JsonArray of map entries represented as JsonObjects.
+     */
+    @Test
+    public void testDeSerializeIntegerArrayStringArrayMapToEntriesArray() {
+        String jsonString = "[" +
+            "    {" +
+            "        \"key\": [1,2]," +
+            "        \"value\": [" +
+            "            \"Bob\"," +
+            "            \"Tom\"" +
+            "        ]" +
+            "    }," +
+            "    {" +
+            "        \"key\": [3,4]," +
+            "        \"value\": [" +
+            "            \"John\"," +
+            "            \"Greg\"" +
+            "        ]" +
+            "    }" +
+           "]";
+        ParameterizedType pt = new ParameterizedType() {
+            @Override
+            public Type[] getActualTypeArguments() {
+                return new Type[] { Integer[].class, String[].class };
+            }
+
+            @Override
+            public Type getRawType() {
+                return Map.class;
+            }
+
+            @Override
+            public Type getOwnerType() {
+                return null;
+            }
+        };
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+        Map<Integer[], String[]> map = jsonb.fromJson(jsonString, pt);
+        assertEquals(2, map.size());
+        // Make sure that all map entries were checked.
+        int valueCheck = 0x00;
+        for (Map.Entry<Integer[], String[]> entry : map.entrySet()) {
+            Integer[] key = entry.getKey();
+            String[] value = entry.getValue();
+            if (key[0] == 1 && key[1] == 2) {
+                assertEquals("Bob" ,value[0]);
+                assertEquals("Tom" ,value[1]);
+                valueCheck |= 0x01;
+            }
+            if (key[0] == 3 && key[1] == 4) {
+                assertEquals("John" ,value[0]);
+                assertEquals("Greg" ,value[1]);
+                valueCheck |= 0x02;
+            }
+        }
+        if ((valueCheck & 0x01) == 0) {
+            fail("Did not find key [1,2] in the Map");
+        }
+        if ((valueCheck & 0x02) == 0) {
+            fail("Did not find key [3,4] in the Map");
+        }
+    }
+
+    /**
+     * Test de-serialization of Map<Trainer[], Pokemon[]>.
+     * Map is stored as an JsonArray of map entries represented as JsonObjects.
+     */
+    @Test
+    public void testDeSerializePoJoArrayPoJoArrayMapToEntriesArray() {
+        String jsonString = "[" +
+                "    {" +
+                "        \"key\": [" +
+                "            {" +
+                "                \"name\": \"Ash\"," +
+                "                \"age\": 12" +
+                "            },{" +
+                "                \"name\": \"Joe\"," +
+                "                \"age\": 14" +
+                "            }" +
+                "        ]," +
+                "        \"value\": [" +
+                "            {" +
+                "                \"name\": \"Rayquaza\"," +
+                "                \"type\": \"dragon\"," +
+                "                \"cp\": 3273" +
+                "            },{" +
+                "                \"name\": \"Tyranitar\"," +
+                "                \"type\": \"dark\"," +
+                "                \"cp\": 3181" +
+                "            }" +
+                "        ]" +
+                "    },{" +
+                "        \"key\": [" +
+                "            {" +
+                "                \"name\": \"Bob\"," +
+                "                \"age\": 13" +
+                "            },{" +
+                "                \"name\": \"Maggie\"," +
+                "                \"age\": 15" +
+                "            }" +
+                "        ]," +
+                "        \"value\": [" +
+                "            {" +
+                "                \"name\": \"Raikou\"," +
+                "                \"type\": \"electric\"," +
+                "                \"cp\": 3095" +
+                "            },{" +
+                "                \"name\": \"Mamoswine\"," +
+                "                \"type\": \"ice\"," +
+                "                \"cp\": 3055" +
+                "            }" +
+                "        ]" +
+                "    }" +
+                "]";
+        ParameterizedType pt = new ParameterizedType() {
+            @Override
+            public Type[] getActualTypeArguments() {
+                return new Type[] { Trainer[].class, Pokemon[].class };
+            }
+
+            @Override
+            public Type getRawType() {
+                return Map.class;
+            }
+
+            @Override
+            public Type getOwnerType() {
+                return null;
+            }
+        };
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+        Map<Trainer[], Pokemon[]> map = jsonb.fromJson(jsonString, pt);
+        assertEquals(2, map.size());
+        int valueCheck = 0x00;
+        for (Map.Entry<Trainer[], Pokemon[]> entry : map.entrySet()) {
+            Trainer[] key = entry.getKey();
+            Pokemon[] value = entry.getValue();
+            if (key[0].name.equals("Ash") && key[1].name.equals("Joe")) {
+                assertEquals(12, key[0].age);
+                assertEquals(14, key[1].age);
+                assertEquals("Rayquaza", value[0].name);
+                assertEquals("dragon", value[0].type);
+                assertEquals(3273, value[0].cp);
+                assertEquals("Tyranitar", value[1].name);
+                assertEquals("dark", value[1].type);
+                assertEquals(3181, value[1].cp);
+                valueCheck |= 0x01;
+            }
+            if (key[0].name.equals("Bob") && key[1].name.equals("Maggie")) {
+                assertEquals(13, key[0].age);
+                assertEquals(15, key[1].age);
+                assertEquals("Raikou", value[0].name);
+                assertEquals("electric", value[0].type);
+                assertEquals(3095, value[0].cp);
+                assertEquals("Mamoswine", value[1].name);
+                assertEquals("ice", value[1].type);
+                assertEquals(3055, value[1].cp);
+                valueCheck |= 0x02;
+            }
+        }
+        if ((valueCheck & 0x01) == 0) {
+            fail("Did not find key with \"Ash\" and \"Joe\" in the Map");
+        }
+        if ((valueCheck & 0x02) == 0) {
+            fail("Did not find key with \"Bob\" and \"Maggie\" in the Map");
+        }
+    }
+
+    public static class LocaleSerializer implements JsonbSerializer<Locale> {
+
+        @Override
+        public void serialize(Locale obj, JsonGenerator generator, SerializationContext ctx) {
+            generator.write(obj.toLanguageTag());
+        }
+    }
+
+    public static class LocaleDeserializer implements JsonbDeserializer<Locale> {
+
+        @Override
+        public Locale deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+            return Locale.forLanguageTag(parser.getString());
+        }
+    }
+
+    public static class MapObject<K, V> {
+
+        private Map<K, V> values;
+
+        public MapObject() {
+            this.values = new HashMap<>();
+        }
+
+        public Map<K, V> getValues() {
+            return values;
+        }
+
+        public void setValues(Map<K, V> values) {
+            if (values == null) {
+                throw new IllegalArgumentException("values cannot be null");
+            }
+            this.values = values;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof MapObject) {
+                MapObject<?,?> to = (MapObject<?,?>) o;
+                return values.equals(to.values);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(this.values);
+        }
+
+        @Override
+        public String toString() {
+            return values.toString();
+        }
+    }
+
+    public static class MapObjectLocaleString extends MapObject<Locale, String> {};
+
+    private void verifyMapObjectLocaleStringSerialization(JsonObject jsonObject, MapObjectLocaleString mapObject) {
+        // Expected serialization is: {"values":[{"key":"lang-tag","value":"string"},...]}
+        assertEquals(1, jsonObject.size());
+        assertNotNull(jsonObject.get("values"));
+        assertEquals(JsonValue.ValueType.ARRAY, jsonObject.get("values").getValueType());
+        JsonArray jsonArray = jsonObject.getJsonArray("values");
+        assertEquals(mapObject.getValues().size(), jsonArray.size());
+        MapObjectLocaleString resObject = new MapObjectLocaleString();
+        for (JsonValue jsonValue : jsonArray) {
+            assertEquals(JsonValue.ValueType.OBJECT, jsonValue.getValueType());
+            JsonObject entry = jsonValue.asJsonObject();
+            assertEquals(2, entry.size());
+            assertNotNull(entry.get("key"));
+            assertEquals(JsonValue.ValueType.STRING, entry.get("key").getValueType());
+            assertNotNull(entry.get("value"));
+            assertEquals(JsonValue.ValueType.STRING, entry.get("value").getValueType());
+            resObject.getValues().put(Locale.forLanguageTag(entry.getString("key")), entry.getString("value"));
+        }
+        assertEquals(mapObject, resObject);
+    }
+
+    /**
+     * Test a Locale/String map with custom Locale serializer and deserializer.
+     */
+    @Test
+    public void testMapLocaleString() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
+                .withSerializers(new LocaleSerializer())
+                .withDeserializers(new LocaleDeserializer()));
+
+        MapObjectLocaleString mapObject = new MapObjectLocaleString();
+        mapObject.getValues().put(Locale.US, "us");
+        mapObject.getValues().put(Locale.ENGLISH, "en");
+        mapObject.getValues().put(Locale.JAPAN, "jp");
+
+        String json = jsonb.toJson(mapObject);
+        JsonObject jsonObject = Json.createReader(new StringReader(json)).read().asJsonObject();
+        verifyMapObjectLocaleStringSerialization(jsonObject, mapObject);
+
+        MapObjectLocaleString resObject = jsonb.fromJson(json, MapObjectLocaleString.class);
+        assertEquals(mapObject, resObject);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/MapToObjectSerializerTest.java b/src/test/java/org/eclipse/yasson/serializers/MapToObjectSerializerTest.java
new file mode 100644
index 0000000..796f3d1
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/MapToObjectSerializerTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Assertions.shouldFail;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.math.BigInteger;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Test various use-cases with {@code Map} serializer which
+ * stores Map as JSON object.
+ */
+public class MapToObjectSerializerTest {
+
+    /**
+     * Enum used as key in maps during tests
+     */
+    enum TestEnum {
+        ONE, TWO;
+
+        @Override
+        /**
+         * Force to lower case to check toString is not used during serialization of maps
+         */
+        public String toString() {
+            return this.name().toLowerCase();
+        }
+    }
+
+    /**
+     * MapObject to test different parametrized maps.
+     * @param <K> The map key
+     * @param <V>The map value
+     */
+    public static class MapObject<K, V> {
+
+        private Map<K, V> values;
+
+        public MapObject() {
+            this.values = new HashMap<>();
+        }
+
+        public Map<K, V> getValues() {
+            return values;
+        }
+
+        public void setValues(Map<K, V> values) {
+            this.values = values;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof MapObject) {
+                MapObject<?,?> to = (MapObject<?,?>) o;
+                return values.equals(to.values);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(this.values);
+        }
+
+        @Override
+        public String toString() {
+            return values.toString();
+        }
+    }
+
+    public static class MapObjectIntegerString extends MapObject<Integer, String> {};
+
+    public static class MapObjectBigIntegerString extends MapObject<BigInteger, String> {};
+
+    public static class MapObjectEnumString extends MapObject<TestEnum, String> {};
+
+    public static class MapObjectStringString extends MapObject<String, String> {};
+
+    public static class MapObjectBooleanString extends MapObject<Boolean, String> {};
+
+    /**
+     * Test serialization of Map with Number keys and String values.
+     */
+    @Test
+    public void testSerializeEnumMapToObject() {
+        Map<TestEnum, Object> map = new EnumMap<>(TestEnum.class);
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
+        map.put(TestEnum.ONE, "value1");
+        map.put(TestEnum.TWO, "value2");
+        String json = jsonb.toJson(map);
+        for (TestEnum e : TestEnum.values()) {
+            assertTrue(json.contains(e.name()), "Enumeration not well serialized");
+        }
+    }
+
+    /**
+     * Test for Integer/String map.
+     */
+    @Test
+    public void testIntegerString() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+
+        MapObjectIntegerString mapObject = new MapObjectIntegerString();
+        mapObject.getValues().put(12, "twelve");
+        mapObject.getValues().put(48, "forty eight");
+        mapObject.getValues().put(256, "two hundred fifty-six");
+
+        String json = jsonb.toJson(mapObject);
+        MapObjectIntegerString resObject = jsonb.fromJson(json, MapObjectIntegerString.class);
+        assertEquals(mapObject, resObject);
+    }
+
+    /**
+     * Test for BigInteger/String map.
+     */
+    @Test
+    public void testBigIntegerString() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+
+        MapObjectBigIntegerString mapObject = new MapObjectBigIntegerString();
+        mapObject.getValues().put(new BigInteger("12"), "twelve");
+        mapObject.getValues().put(new BigInteger("48"), "forty eight");
+        mapObject.getValues().put(new BigInteger("256"), "two hundred fifty-six");
+
+        String json = jsonb.toJson(mapObject);
+        MapObjectBigIntegerString resObject = jsonb.fromJson(json, MapObjectBigIntegerString.class);
+        assertEquals(mapObject, resObject);
+    }
+
+    /**
+     * Test for Enum/String map.
+     */
+    @Test
+    public void testEnumString() {
+        Jsonb jsonb = JsonbBuilder.create();
+
+        MapObjectEnumString mapObject = new MapObjectEnumString();
+        mapObject.getValues().put(TestEnum.ONE, "one");
+        mapObject.getValues().put(TestEnum.TWO, "two");
+
+        String json = jsonb.toJson(mapObject);
+        MapObjectEnumString resObject = jsonb.fromJson(json, MapObjectEnumString.class);
+        assertEquals(mapObject, resObject);
+    }
+
+    /**
+     * Test for String/String map.
+     */
+    @Test
+    public void testStringString() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().setProperty("lala", "lala"));
+
+        MapObjectStringString mapObject = new MapObjectStringString();
+        mapObject.getValues().put("one", "one");
+        mapObject.getValues().put("two", "two");
+
+        String json = jsonb.toJson(mapObject);
+        MapObjectStringString resObject = jsonb.fromJson(json, MapObjectStringString.class);
+        assertEquals(mapObject, resObject);
+    }
+
+    /**
+     * Test for a non parametrized map that should use Strings as keys.
+     */
+    @Test
+    public void testNotParametrizedMap() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+
+        Map<Integer, String> mapObject = new HashMap<>();
+        mapObject.put(12, "twelve");
+        mapObject.put(48, "forty eight");
+        mapObject.put(256, "two hundred fifty-six");
+
+        String json = jsonb.toJson(mapObject);
+        Map resObject = jsonb.fromJson(json, Map.class);
+        assertEquals(3, resObject.size());
+        assertTrue(resObject.keySet().iterator().next() instanceof String);
+    }
+
+    /**
+     * Test for Boolean/String map. This map is not even generated by the
+     * MapToObjectSerializer as a boolean is not managed by that serializer.
+     * But the json string should be deserialized in the same way.
+     */
+    @Test
+    public void testBooleanStringMapToObjectSerializer() {
+        Jsonb jsonb = JsonbBuilder.create();
+
+        String json = "{\"values\":{\"true\":\"TRUE\",\"false\":\"FALSE\"}}";
+        MapObjectBooleanString resObject = jsonb.fromJson(json, MapObjectBooleanString.class);
+        assertEquals(2, resObject.getValues().size());
+        assertEquals("TRUE", resObject.getValues().get(true));
+        assertEquals("FALSE", resObject.getValues().get(false));
+    }
+
+    /**
+     * Test for Integer/String map but giving an incorrect integer key.
+     * JsonbException is expected.
+     */
+    @Test
+    public void testIncorrectTypeMapToObjectSerializer() {
+        Jsonb jsonb = JsonbBuilder.create();
+
+        String json = "{\"values\":{\"1\":\"OK\",\"error\":\"KO\"}}";
+        shouldFail(() -> jsonb.fromJson(json, MapObjectIntegerString.class));
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java b/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java
new file mode 100644
index 0000000..db0efdf
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/SerializersTest.java
@@ -0,0 +1,815 @@
+/*
+ * Copyright (c) 2016, 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers;
+
+import java.io.StringReader;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TimeZone;
+import java.util.TreeMap;
+
+import jakarta.json.Json;
+import jakarta.json.JsonObject;
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import jakarta.json.bind.JsonbConfig;
+import jakarta.json.bind.JsonbException;
+import jakarta.json.bind.config.PropertyOrderStrategy;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+
+import org.eclipse.yasson.TestTypeToken;
+import org.eclipse.yasson.YassonConfig;
+import org.eclipse.yasson.internal.model.ReverseTreeMap;
+import org.eclipse.yasson.serializers.model.AnnotatedGenericWithSerializerType;
+import org.eclipse.yasson.serializers.model.AnnotatedWithSerializerType;
+import org.eclipse.yasson.serializers.model.Author;
+import org.eclipse.yasson.serializers.model.Box;
+import org.eclipse.yasson.serializers.model.BoxWithAnnotations;
+import org.eclipse.yasson.serializers.model.Containee;
+import org.eclipse.yasson.serializers.model.Container;
+import org.eclipse.yasson.serializers.model.Crate;
+import org.eclipse.yasson.serializers.model.CrateDeserializer;
+import org.eclipse.yasson.serializers.model.CrateDeserializerWithConversion;
+import org.eclipse.yasson.serializers.model.CrateInner;
+import org.eclipse.yasson.serializers.model.CrateJsonObjectDeserializer;
+import org.eclipse.yasson.serializers.model.CrateSerializer;
+import org.eclipse.yasson.serializers.model.CrateSerializerWithConversion;
+import org.eclipse.yasson.serializers.model.ExplicitJsonbSerializer;
+import org.eclipse.yasson.serializers.model.GenericPropertyPojo;
+import org.eclipse.yasson.serializers.model.ImplicitJsonbSerializer;
+import org.eclipse.yasson.serializers.model.NumberDeserializer;
+import org.eclipse.yasson.serializers.model.NumberSerializer;
+import org.eclipse.yasson.serializers.model.RecursiveDeserializer;
+import org.eclipse.yasson.serializers.model.RecursiveSerializer;
+import org.eclipse.yasson.serializers.model.SimpleAnnotatedSerializedArrayContainer;
+import org.eclipse.yasson.serializers.model.SimpleContainer;
+import org.eclipse.yasson.serializers.model.StringWrapper;
+import org.eclipse.yasson.serializers.model.SupertypeSerializerPojo;
+import org.junit.jupiter.api.Test;
+
+import static java.util.Collections.singletonMap;
+
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.eclipse.yasson.Jsonbs.nullableJsonb;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class SerializersTest {
+
+    private static final JsonbSerializer<Box> BOX_ARRAY_SERIALIZER_CHAINED = new JsonbSerializer<>() {
+        public void serialize(Box box, JsonGenerator out, SerializationContext ctx) {
+            out.writeStartArray()
+                    .write(box.boxStr)
+                    .write(box.secondBoxStr)
+                    .writeEnd();
+        }
+    };
+
+    private static final JsonbSerializer<Box> BOX_ARRAY_SERIALIZER = new JsonbSerializer<>() {
+        public void serialize(Box box, JsonGenerator out, SerializationContext ctx) {
+            out.writeStartArray();
+            out.write(box.boxStr);
+            out.write(box.secondBoxStr);
+            out.writeEnd();
+        }
+    };
+
+    @Test
+    public void testClassLevelAnnotation() {
+        Crate crate = new Crate();
+        crate.crateBigDec = BigDecimal.TEN;
+        crate.crateStr = "crateStr";
+
+        crate.annotatedType = new AnnotatedWithSerializerType();
+        crate.annotatedType.value = "abc";
+        crate.annotatedGenericType = new AnnotatedGenericWithSerializerType<>();
+        crate.annotatedGenericType.value = new Crate();
+        crate.annotatedGenericType.value.crateStr = "inside generic";
+        crate.annotatedTypeOverriddenOnProperty = new AnnotatedWithSerializerType();
+        crate.annotatedTypeOverriddenOnProperty.value = "def";
+        String expected = "{\"annotatedGenericType\":{\"generic\":{\"crate_str\":\"inside generic\"}},\"annotatedType\":{\"valueField\":\"replaced value\"},\"annotatedTypeOverriddenOnProperty\":{\"valueField\":\"overridden value\"},\"crateBigDec\":10,\"crate_str\":\"crateStr\"}";
+
+        assertEquals(expected, defaultJsonb.toJson(crate));
+
+        Crate result = defaultJsonb.fromJson(expected, Crate.class);
+        assertEquals("replaced value", result.annotatedType.value);
+        assertEquals("overridden value", result.annotatedTypeOverriddenOnProperty.value);
+        assertEquals("inside generic", result.annotatedGenericType.value.crateStr);
+    }
+
+    @Test
+    public void testClassLevelAnnotationOnRoot() {
+        AnnotatedWithSerializerType annotatedType = new AnnotatedWithSerializerType();
+        annotatedType.value = "abc";
+        String expected = "{\"valueField\":\"replaced value\"}";
+
+        assertEquals(expected, defaultJsonb.toJson(annotatedType));
+
+        AnnotatedWithSerializerType result = defaultJsonb.fromJson(expected, AnnotatedWithSerializerType.class);
+        assertEquals("replaced value", result.value);
+    }
+
+    @Test
+    public void testClassLevelAnnotationOnGenericRoot() {
+        AnnotatedGenericWithSerializerType<Crate> annotatedType = new AnnotatedGenericWithSerializerType<>();
+        annotatedType.value = new Crate();
+        annotatedType.value.crateStr = "inside generic";
+        String expected = "{\"generic\":{\"crate_str\":\"inside generic\"}}";
+
+        assertEquals(expected, defaultJsonb.toJson(annotatedType));
+
+        AnnotatedGenericWithSerializerType<Crate> result = defaultJsonb.fromJson(expected, new AnnotatedGenericWithSerializerType<Crate>(){}.getClass().getGenericSuperclass());
+        assertEquals("inside generic", result.value.crateStr);
+    }
+
+    /**
+     * Tests JSONB deserialization of arbitrary type invoked from a Deserializer.
+     */
+    @Test
+    public void testDeserializerDeserializationByType() {
+        JsonbConfig config = new JsonbConfig().withDeserializers(new CrateDeserializer());
+        Jsonb jsonb = JsonbBuilder.create(config);
+
+        Box box = createPojoWithDates();
+
+        String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"date\":\"2015-05-14T11:10:01\"},\"secondBoxStr\":\"Second box string\"}";
+
+        Box result = jsonb.fromJson(expected, Box.class);
+
+        //deserialized by deserializationContext.deserialize(Class c)
+        assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec);
+        assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr);
+
+        assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr);
+        assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr);
+
+        //set by deserializer statically
+        assertEquals(new BigDecimal("123"), result.crate.crateBigDec);
+        assertEquals("abc", result.crate.crateStr);
+    }
+
+    /**
+     * Tests JSONB serialization of arbitrary type invoked from a Serializer.
+     */
+    @Test
+    public void testSerializerSerializationOfType() {
+        JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializer());
+        Jsonb jsonb = JsonbBuilder.create(config);
+        String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}";
+        Box pojo = createPojo();
+
+        assertEquals(expected, jsonb.toJson(pojo));
+
+        Box result = jsonb.fromJson(expected, Box.class);
+        assertEquals(new BigDecimal("54321"), result.crate.crateBigDec);
+        //result.crate.crateStr is mapped to crate_str by jsonb property
+        assertNull(result.crate.crateStr);
+        assertEquals(pojo.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr);
+        assertEquals(pojo.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec);
+    }
+
+    @Test
+    public void testSerializerSerializationOfTypeWithExplicitType() {
+        JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializer());
+        Jsonb jsonb = JsonbBuilder.create(config);
+        String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}";
+        Box pojo = createPojo();
+
+        assertEquals(expected, jsonb.toJson(pojo, Box.class));
+
+        Box result = jsonb.fromJson(expected, Box.class);
+        assertEquals(new BigDecimal("54321"), result.crate.crateBigDec);
+        //result.crate.crateStr is mapped to crate_str by jsonb property
+        assertNull(result.crate.crateStr);
+        assertEquals(pojo.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr);
+        assertEquals(pojo.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec);
+    }
+
+    /**
+     * Tests jsonb type conversion, including property customization.
+     */
+    @Test
+    public void testDeserializersUsingConversion() {
+        JsonbConfig config = new JsonbConfig().withDeserializers(new CrateDeserializerWithConversion());
+        Jsonb jsonb = JsonbBuilder.create(config);
+
+        String json = "{\"boxStr\":\"Box string\",\"crate\":{\"date-converted\":\"2015-05-14T11:10:01\",\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}";
+        Box result = jsonb.fromJson(json, Box.class);
+        final Date expected = getExpectedDate();
+        assertEquals(expected, result.crate.date);
+        assertEquals("Box string", result.boxStr);
+        assertEquals("Second box string", result.secondBoxStr);
+    }
+
+    @Test
+    public void testCrateJsonObjectDeserializer() {
+        JsonbConfig config = new JsonbConfig().withDeserializers(new CrateJsonObjectDeserializer());
+        Jsonb jsonb = JsonbBuilder.create(config);
+        String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"date-converted\":\"2015-05-14T11:10:01\",\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crateInnerStr\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321},\"secondBoxStr\":\"Second box string\"}";
+        Box result = jsonb.fromJson(expected, Box.class);
+        assertEquals(new BigDecimal("54321"), result.crate.crateBigDec);
+        assertEquals("REPLACED crate str", result.crate.crateStr);
+        assertEquals("Single inner", result.crate.crateInner.crateInnerStr);
+        assertEquals(BigDecimal.TEN, result.crate.crateInner.crateInnerBigDec);
+    }
+
+    private static Date getExpectedDate() {
+        return new Calendar.Builder().setDate(2015, 4, 14).setTimeOfDay(11, 10, 1).setTimeZone(TimeZone.getTimeZone("Z")).build().getTime();
+    }
+
+    @Test
+    public void testSerializationUsingConversion() {
+        JsonbConfig config = new JsonbConfig().withSerializers(new CrateSerializerWithConversion());
+        Jsonb jsonb = JsonbBuilder.create(config);
+
+        String json = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\",\"date\":\"14.05.2015 || 11:10:01\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}";
+        assertEquals(json, jsonb.toJson(createPojoWithDates()));
+    }
+
+    @Test
+    public void testAnnotations() {
+        BoxWithAnnotations box = new BoxWithAnnotations();
+        box.boxStr = "Box string";
+        box.secondBoxStr = "Second box string";
+        box.crate = new Crate();
+        box.crate.date = getExpectedDate();
+        box.crate.crateInner = createCrateInner("Single inner");
+
+        box.crate.crateInnerList = new ArrayList<>();
+        box.crate.crateInnerList.add(createCrateInner("List inner 0"));
+        box.crate.crateInnerList.add(createCrateInner("List inner 1"));
+
+        String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}";
+
+        assertEquals(expected, defaultJsonb.toJson(box));
+
+        BoxWithAnnotations result = defaultJsonb.fromJson(expected, BoxWithAnnotations.class);
+
+        //deserialized by deserializationContext.deserialize(Class c)
+        assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec);
+        assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr);
+
+        assertEquals(2L, result.crate.crateInnerList.size());
+        assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr);
+        assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr);
+
+        //set by deserializer statically
+        assertEquals(new BigDecimal("123"), result.crate.crateBigDec);
+        assertEquals("abc", result.crate.crateStr);
+    }
+
+    @Test
+    public void testAnnotationsOverride() {
+        JsonbConfig config = new JsonbConfig().withDeserializers(new CrateJsonObjectDeserializer()).withSerializers(new CrateSerializer());
+        Jsonb jsonb = JsonbBuilder.create(config);
+
+        BoxWithAnnotations box = new BoxWithAnnotations();
+        box.boxStr = "Box string";
+        box.secondBoxStr = "Second box string";
+        box.crate = new Crate();
+        box.crate.crateInner = createCrateInner("Single inner");
+        box.crate.date = getExpectedDate();
+
+        box.crate.crateInnerList = new ArrayList<>();
+        box.crate.crateInnerList.add(createCrateInner("List inner 0"));
+        box.crate.crateInnerList.add(createCrateInner("List inner 1"));
+
+        String expected = "{\"boxStr\":\"Box string\",\"crate\":{\"crateStr\":\"REPLACED crate str\",\"crateInner\":{\"crateInnerBigDec\":10,\"crate_inner_str\":\"Single inner\"},\"crateInnerList\":[{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 0\"},{\"crateInnerBigDec\":10,\"crate_inner_str\":\"List inner 1\"}],\"crateBigDec\":54321,\"date-converted\":\"2015-05-14T11:10:01Z[UTC]\"},\"secondBoxStr\":\"Second box string\"}";
+
+        assertEquals(expected, jsonb.toJson(box));
+
+        BoxWithAnnotations result = jsonb.fromJson(expected, BoxWithAnnotations.class);
+
+        //deserialized by deserializationContext.deserialize(Class c)
+        assertEquals(box.crate.crateInner.crateInnerBigDec, result.crate.crateInner.crateInnerBigDec);
+        assertEquals(box.crate.crateInner.crateInnerStr, result.crate.crateInner.crateInnerStr);
+
+        assertEquals(2L, result.crate.crateInnerList.size());
+        assertEquals("List inner 0", result.crate.crateInnerList.get(0).crateInnerStr);
+        assertEquals("List inner 1", result.crate.crateInnerList.get(1).crateInnerStr);
+
+        //set by deserializer statically
+        assertEquals(new BigDecimal("123"), result.crate.crateBigDec);
+        assertEquals("abc", result.crate.crateStr);
+    }
+
+    @Test
+    public void testStringField() {
+        StringWrapper pojo = new StringWrapper();
+        pojo.strField = "abc";
+        final String result = defaultJsonb.toJson(pojo);
+        assertEquals("{\"strField\":\"   abc\"}", result);
+    }
+
+    @Test
+    public void testContainerSerializer() {
+        SimpleAnnotatedSerializedArrayContainer container = new SimpleAnnotatedSerializedArrayContainer();
+        SimpleContainer instance1 = new SimpleContainer();
+        instance1.setInstance("Test String 1");
+        SimpleContainer instance2 = new SimpleContainer();
+        instance2.setInstance("Test String 2");
+        container.setArrayInstance(new SimpleContainer[] {instance1, instance2});
+
+        container.setListInstance(new ArrayList<>());
+        container.getListInstance().add(new SimpleContainer("Test List 1"));
+        container.getListInstance().add(new SimpleContainer("Test List 2"));
+
+        String jsonString = defaultJsonb.toJson(container);
+        assertEquals("{\"arrayInstance\":[{\"instance\":\"Test String 1\"},{\"instance\":\"Test String 2\"}],\"listInstance\":[{\"instance\":\"Test List 1\"},{\"instance\":\"Test List 2\"}]}", jsonString);
+
+        SimpleAnnotatedSerializedArrayContainer unmarshalledObject = defaultJsonb.fromJson("{\"arrayInstance\":[{\"instance\":\"Test String 1\"},{\"instance\":\"Test String 2\"}],\"listInstance\":[{\"instance\":\"Test List 1\"},{\"instance\":\"Test List 2\"}]}", SimpleAnnotatedSerializedArrayContainer.class);
+
+        assertEquals("Test String 1", unmarshalledObject.getArrayInstance()[0].getInstance());
+        assertEquals("Test String 2", unmarshalledObject.getArrayInstance()[1].getInstance());
+
+        assertEquals("Test List 1", unmarshalledObject.getListInstance().get(0).getInstance());
+        assertEquals("Test List 2", unmarshalledObject.getListInstance().get(1).getInstance());
+    }
+
+    /**
+     * Tests avoiding StackOverflowError.
+     */
+    @Test
+    public void testRecursiveSerializer() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new RecursiveSerializer()).withDeserializers(new RecursiveDeserializer()));
+
+        Box box = new Box();
+        box.boxStr = "Box to serialize";
+        try {
+            jsonb.toJson(box);
+            fail();
+        } catch (JsonbException ex) {
+        }
+
+        try {
+            jsonb.fromJson("{\"boxStr\":\"Box to deserialize\"}", Box.class);
+            fail();
+        } catch (StackOverflowError error){
+        }
+    }
+
+    @Test
+    public void testAuthor() {
+        Author author = new Author("Sarah", "Connor");
+        String expected = "{\"firstName\":\"S\",\"lastName\":\"Connor\"}";
+        String json = defaultJsonb.toJson(author);
+
+        assertEquals(expected, json);
+
+        Author result = defaultJsonb.fromJson(expected, Author.class);
+        assertEquals("John", result.getFirstName());
+        assertEquals("Connor", result.getLastName());
+    }
+
+    @Test
+    public void testSupertypeSerializer() {
+        Jsonb jsonb = JsonbBuilder.create(
+                new JsonbConfig().withSerializers(new NumberSerializer())
+                        .withDeserializers(new NumberDeserializer()));
+        SupertypeSerializerPojo pojo = new SupertypeSerializerPojo();
+        pojo.setNumberInteger(10);
+        pojo.setAnotherNumberInteger(11);
+        assertEquals("{\"anotherNumberInteger\":\"12\",\"numberInteger\":\"11\"}", jsonb.toJson(pojo));
+
+        pojo = jsonb.fromJson("{\"anotherNumberInteger\":\"12\",\"numberInteger\":\"11\"}", SupertypeSerializerPojo.class);
+        assertEquals(Integer.valueOf(10), pojo.getNumberInteger());
+        assertEquals(Integer.valueOf(11), pojo.getAnotherNumberInteger());
+    }
+    
+    @Test
+    public void testObjectDeserializerWithLexOrderStrategy() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL));
+        Object pojo = jsonb.fromJson("{\"first\":{},\"third\":{},\"second\":{\"second\":2,\"first\":1}}", Object.class);
+        assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap");
+        @SuppressWarnings("unchecked")
+        SortedMap<String, Object> pojoAsMap = (SortedMap<String, Object>) pojo;
+        assertTrue(pojoAsMap.get("second") instanceof TreeMap, "Pojo inner object is not of type TreeMap");
+        assertEquals("{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}", jsonb.toJson(pojo));
+    }
+    
+    @Test
+    public void testObjectDeserializerWithReverseOrderStrategy() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE));
+        Object pojo = jsonb.fromJson("{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}", Object.class);
+        assertTrue(pojo instanceof ReverseTreeMap, "Pojo is not of type ReverseTreeMap");
+        @SuppressWarnings("unchecked")
+        SortedMap<String, Object> pojoAsMap = (SortedMap<String, Object>) pojo;
+        assertTrue(pojoAsMap.get("second") instanceof TreeMap, "Pojo inner object is not of type TreeMap");
+        assertEquals("{\"third\":{},\"second\":{\"second\":2,\"first\":1},\"first\":{}}", jsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testObjectDeserializerWithAnyOrNoneOrderStrategy() {
+        String json = "{\"first\":{},\"second\":{\"first\":1,\"second\":2},\"third\":{}}";
+        // ANY
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY));
+        Object pojo = jsonb.fromJson(json, Object.class);
+        assertTrue(pojo instanceof HashMap, "Pojo is not of type HashMap with \"ANY\" strategy");
+        // none
+        pojo = defaultJsonb.fromJson(json, Object.class);
+        assertTrue(pojo instanceof HashMap, "Pojo is not of type HashMap with no strategy");
+    }
+
+    @Test
+    public void testSortedMapDerializer() {
+        String json = "{\"first\":1,\"third\":3,\"second\":2}";
+
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY));
+        SortedMap<?, ?> pojo = jsonb.fromJson(json, SortedMap.class);
+        assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with \"ANY\" strategy");
+
+        jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL));
+        pojo = jsonb.fromJson(json, SortedMap.class);
+        assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with no strategy");
+        assertEquals("{\"first\":1,\"second\":2,\"third\":3}", jsonb.toJson(pojo));
+
+        jsonb = JsonbBuilder.create(new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.REVERSE));
+        pojo = jsonb.fromJson(json, SortedMap.class);
+        assertTrue(pojo instanceof ReverseTreeMap, "Pojo is not of type ReverseTreeMap with no strategy");
+        assertEquals("{\"third\":3,\"second\":2,\"first\":1}", jsonb.toJson(pojo));
+
+        pojo = defaultJsonb.fromJson(json, SortedMap.class);
+        assertTrue(pojo instanceof TreeMap, "Pojo is not of type TreeMap with no strategy");
+        assertEquals("{\"first\":1,\"second\":2,\"third\":3}", defaultJsonb.toJson(pojo));
+    }
+
+    @Test
+    public void testGenericPropertyPojoSerializer() {
+        GenericPropertyPojo<Number> numberPojo = new GenericPropertyPojo<>();
+        numberPojo.setProperty(10L);
+        GenericPropertyPojo<String> stringPojo = new GenericPropertyPojo<>();
+        stringPojo.setProperty("String property");
+
+        String numResult = defaultJsonb.toJson(numberPojo, new TestTypeToken<GenericPropertyPojo<Number>>(){}.getType());
+        assertEquals("{\"propertyByUserSerializer\":\"Number value [10]\"}", numResult);
+
+        String strResult = defaultJsonb.toJson(stringPojo, new TestTypeToken<GenericPropertyPojo<String>>(){}.getType());
+        // because GenericPropertyPojo is annotated to use GenericPropertyPojoSerializer, it will always be
+        // used, despite the fact that the runtime type supplied does not match the serializer type
+        assertEquals("{\"propertyByUserSerializer\":\"Number value [String property]\"}", strResult);
+    }
+
+    @Test
+    public void testSerializeMapWithNulls() {
+        assertEquals("{\"null\":null}", nullableJsonb.toJson(singletonMap(null, null)));
+        assertEquals("{\"key\":null}", nullableJsonb.toJson(singletonMap("key", null)));
+        assertEquals("{\"null\":\"value\"}", nullableJsonb.toJson(singletonMap(null, "value")));
+    }
+
+    @Test
+    public void testSerializeMapWithNullsForceArraySerializer() {
+        Jsonb jsonb = JsonbBuilder.create(new YassonConfig()
+                .withForceMapArraySerializerForNullKeys(Boolean.TRUE)
+                .withNullValues(Boolean.TRUE));
+        assertEquals("[{\"key\":null,\"value\":null}]", jsonb.toJson(singletonMap(null, null)));
+        assertEquals("{\"key\":null}", jsonb.toJson(singletonMap("key", null)));
+        assertEquals("[{\"key\":null,\"value\":\"value\"}]", jsonb.toJson(singletonMap(null, "value")));
+    }
+
+    /**
+     * Test serialization of Map with String keys only.
+     * Map shall be stored as a single JsonObject with keys as object properties names.
+     */
+    @Test
+    public void testSerializeMapToJsonObject() {
+        Map<String, Object> map = new HashMap<>();
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig());
+        map.put("name", "John SMith");
+        map.put("age", 35);
+        map.put("married", true);
+        String json = jsonb.toJson(map);
+        JsonObject jobj = Json.createReader(new StringReader(json)).read().asJsonObject();
+        assertEquals("John SMith", jobj.getString("name"));
+        assertEquals(35, jobj.getInt("age"));
+        assertEquals(true, jobj.getBoolean("married"));
+    }
+
+    @Test
+    public void testDeserializeArrayWithAdvancingParserAfterObjectEnd() {
+        String json = "[{\"stringProperty\":\"Property 1 value\"},{\"stringProperty\":\"Property 2 value\"}]";
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(new SimplePojoDeserializer()));
+        SimplePojo[] result = jsonb.fromJson(json, SimplePojo[].class);
+        assertEquals(2, result.length);
+    }
+
+    public class SimplePojoDeserializer implements JsonbDeserializer<SimplePojo> {
+        @Override
+        public SimplePojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+            //parser.getObject advances the parser to END_OBJECT.
+            JsonObject json = parser.getObject();
+            SimplePojo simplePojo = new SimplePojo();
+            simplePojo.setStringProperty(json.getString("stringProperty"));
+            return simplePojo;
+        }
+    }
+
+    @Test
+    public void testDeserializeArrayWithAdvancingParserAfterObjectEndUsingValue() {
+        String json = "[{\"stringProperty\":\"Property 1 value\"},{\"stringProperty\":\"Property 2 value\"}]";
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(new SimplePojoValueDeserializer()));
+        SimplePojo[] result = jsonb.fromJson(json, SimplePojo[].class);
+        assertEquals(2, result.length);
+    }
+
+    public class SimplePojoValueDeserializer implements JsonbDeserializer<SimplePojo> {
+        @Override
+        public SimplePojo deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+            //parser.getValue advances the parser to END_OBJECT in case of object.
+            JsonObject json = parser.getValue().asJsonObject();
+            SimplePojo simplePojo = new SimplePojo();
+            simplePojo.setStringProperty(json.getString("stringProperty"));
+            return simplePojo;
+        }
+    }
+
+    public class SimplePojo {
+        private String stringProperty;
+
+        public String getStringProperty() {
+            return stringProperty;
+        }
+
+        public void setStringProperty(String stringProperty) {
+            this.stringProperty = stringProperty;
+        }
+    }
+
+    private static Box createPojoWithDates() {
+        Date date = getExpectedDate();
+        Box box = createPojo();
+        box.crate.date = date;
+        box.crate.crateInner.date = date;
+        return box;
+    }
+
+    private static Box createPojo() {
+        Box box = new Box();
+        box.boxStr = "Box string";
+        box.crate = new Crate();
+        box.secondBoxStr = "Second box string";
+
+
+        box.crate.crateInner = createCrateInner("Single inner");
+
+        box.crate.crateInnerList = new ArrayList<>();
+        box.crate.crateInnerList.add(createCrateInner("List inner 0"));
+        box.crate.crateInnerList.add(createCrateInner("List inner 1"));
+
+        return box;
+    }
+
+    private static CrateInner createCrateInner(String name) {
+        final CrateInner crateInner = new CrateInner();
+        crateInner.crateInnerStr = name;
+        crateInner.crateInnerBigDec = BigDecimal.TEN;
+        return crateInner;
+    }
+    
+    public static class Foo { }
+
+    public static class Bar extends Foo { }
+    
+    public static class Baz extends Bar { }
+
+    public static class FooSerializer implements JsonbSerializer<Foo> {
+      public void serialize(Foo obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.write("foo");
+      }
+    }
+
+    public static class BazSerializer implements JsonbSerializer<Baz> {
+      public void serialize(Baz obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.write("baz");
+      }
+    }
+    
+    /**
+     * Test for issue: https://github.com/quarkusio/quarkus/issues/8925
+     * Ensure that if multiple customizations (serializer, deserializer, or adapter) are applied 
+     * for different types in the same class hierarchy, we use the customization for the most 
+     * specific type in the class hierarchy.
+     */
+    @Test
+    public void testSerializerMatching() {
+      Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
+          .withSerializers(new FooSerializer(), new BazSerializer()));
+      assertEquals("\"foo\"", jsonb.toJson(new Foo()));
+      // Since 'Bar' does not have its own serializer, it should use 
+      // the next serializer in the tree (FooSerializer)
+      assertEquals("\"foo\"", jsonb.toJson(new Bar()));
+      assertEquals("\"baz\"", jsonb.toJson(new Baz()));
+    }
+    
+    public static interface One { }
+    public static interface Two { }
+
+    public static interface Three { }
+    
+    public static class OneTwo implements One, Two { }
+    public static class OneTwoThree implements One, Two, Three { }
+    
+    public static class OneSerializer implements JsonbSerializer<One> {
+      public void serialize(One obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.write("one");
+      }
+    }
+    
+    public static class TwoSerializer implements JsonbSerializer<Two> {
+      public void serialize(Two obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.write("two");
+      }
+    }
+    
+    public static class ThreeSerializer implements JsonbSerializer<Three> {
+      public void serialize(Three obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.write("three");
+      }
+    }
+    
+    @Test
+    public void testSerializerMatchingInterfaces01() {
+      Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
+          .withSerializers(new OneSerializer(), new TwoSerializer(), new ThreeSerializer()));
+      assertEquals("\"one\"", jsonb.toJson(new OneTwo()));
+      assertEquals("\"one\"", jsonb.toJson(new OneTwoThree()));
+    }
+    
+    @Test
+    public void testSerializerMatchingInterfaces02() {
+      Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
+          .withSerializers(new ThreeSerializer(), new TwoSerializer()));
+      assertEquals("\"two\"", jsonb.toJson(new OneTwo()));
+      assertEquals("\"two\"", jsonb.toJson(new OneTwoThree()));
+    }
+
+    public class GenericBean<T> {
+
+        public T value;
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof GenericBean){
+                return Objects.equals(GenericBean.class.cast(obj).value, this.value);
+            }
+            return Boolean.FALSE;
+        }
+
+    }
+
+    public class GenericBeanSerializer implements JsonbSerializer<GenericBean> {
+
+        private Boolean called = Boolean.FALSE;
+
+        @Override
+        public void serialize(GenericBean t, JsonGenerator jg, SerializationContext sc) {
+            called = Boolean.TRUE;
+            jg.writeStartObject();
+            sc.serialize("value", t.value, jg);
+            jg.writeEnd();
+        }
+    }
+
+    public class GenericBeanDeserializer implements JsonbDeserializer<GenericBean> {
+
+        private Boolean called = Boolean.FALSE;
+
+        @Override
+        public GenericBean deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+            called = Boolean.TRUE;
+            JsonObject json = parser.getObject();
+            GenericBean<String> bean = new GenericBean<>();
+            bean.value = json.getString("value");
+            return bean;
+        }
+    }
+
+    @Test
+    public void testCustomDeserializerWithParameterizedType() {
+
+        GenericBeanSerializer genericBeanSerializer = new GenericBeanSerializer();
+        GenericBeanDeserializer genericBeanDeserializer = new GenericBeanDeserializer();
+
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(genericBeanDeserializer).withSerializers(genericBeanSerializer));
+
+        GenericBean<String> bean1 = new GenericBean<>();
+        bean1.value = "test1";
+        GenericBean<String> bean2 = new GenericBean<>();
+        bean2.value = "test2";
+        GenericBean<String> bean3 = new GenericBean<>();
+        bean3.value = "test3";
+
+        Collection<GenericBean<String>> asList = Arrays.asList(bean1, bean2, bean3);
+
+        String toJson = jsonb.toJson(asList);
+
+        assertEquals(toJson, "[{\"value\":\"test1\"},{\"value\":\"test2\"},{\"value\":\"test3\"}]");
+        assertTrue(genericBeanSerializer.called);
+
+        List<GenericBean<String>> fromJson = jsonb.fromJson(
+                toJson,
+                new ParameterizedType() {
+            @Override
+            public Type[] getActualTypeArguments() {
+                return new Type[]{GenericBean.class};
+            }
+
+            @Override
+            public Type getRawType() {
+                return Collection.class;
+            }
+
+            @Override
+            public Type getOwnerType() {
+                return null;
+            }
+        }
+        );
+
+        assertEquals(asList, fromJson);
+        assertTrue(genericBeanDeserializer.called);
+
+    }
+
+    @Test
+    public void testImplicitJsonbSerializers() {
+        Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new ExplicitJsonbSerializer()));
+        String expected = "{\"value\":\"123\"}";
+        Box box = new Box();
+        box.boxStr = "Box";
+        assertEquals(expected, jsonb.toJson(box));
+
+        jsonb = JsonbBuilder.create(new JsonbConfig().withSerializers(new ImplicitJsonbSerializer()));
+        assertEquals(expected, jsonb.toJson(box));
+    }
+
+    @Test
+    public void testBoxToArrayChained() {
+        JsonbConfig cfg = new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER_CHAINED);
+        Jsonb jsonb = JsonbBuilder.create(cfg);
+        Box box = new Box();
+        box.boxStr = "str1";
+        box.secondBoxStr = "str2";
+        String expected = "[\"str1\",\"str2\"]";
+
+        assertThat(jsonb.toJson(box), is(expected));
+    }
+
+    @Test
+    public void testBoxToArray() {
+        JsonbConfig cfg = new JsonbConfig().withSerializers(BOX_ARRAY_SERIALIZER);
+        Jsonb jsonb = JsonbBuilder.create(cfg);
+        Box box = new Box();
+        box.boxStr = "str1";
+        box.secondBoxStr = "str2";
+        String expected = "[\"str1\",\"str2\"]";
+
+        assertThat(jsonb.toJson(box), is(expected));
+    }
+
+    @Test
+    public void testCustomSerializersInContainer(){
+        Jsonb jsonb = JsonbBuilder.create();
+
+        Container expected = new Container(List.of(new Containee("k", "v")));
+
+        String expectedJson = jsonb.toJson(expected);
+        System.out.println(expectedJson);
+
+        assertEquals(expected, jsonb.fromJson(expectedJson, Container.class));
+
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/AbstractJsonbSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/AbstractJsonbSerializer.java
new file mode 100644
index 0000000..1224bfa
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/AbstractJsonbSerializer.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.Json;
+import jakarta.json.JsonStructure;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Abstract serializer.
+ */
+public class AbstractJsonbSerializer<T> implements JsonbSerializer<T> {
+    @Override
+    public void serialize(T obj, JsonGenerator generator, SerializationContext ctx) {
+        JsonStructure json = Json.createObjectBuilder().add("value", "123").build();
+        generator.write(json);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerType.java b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerType.java
new file mode 100644
index 0000000..aebe77a
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerType.java
@@ -0,0 +1,23 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+
+@JsonbTypeSerializer(AnnotatedGenericWithSerializerTypeSerializer.class)
+@JsonbTypeDeserializer(AnnotatedGenericWithSerializerTypeDeserializer.class)
+public class AnnotatedGenericWithSerializerType<T> {
+
+    public T value;
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerTypeDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerTypeDeserializer.java
new file mode 100644
index 0000000..14622bb
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerTypeDeserializer.java
@@ -0,0 +1,35 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import jakarta.json.stream.JsonParser.Event;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+public class AnnotatedGenericWithSerializerTypeDeserializer implements JsonbDeserializer<AnnotatedGenericWithSerializerType<?>> {
+
+    @Override
+    public AnnotatedGenericWithSerializerType<?> deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        AnnotatedGenericWithSerializerType<?> result = new AnnotatedGenericWithSerializerType<>();
+        assertEquals(Event.KEY_NAME, parser.next());
+        assertEquals("generic", parser.getString());
+        result.value = ctx.deserialize(((ParameterizedType) rtType).getActualTypeArguments()[0], parser);
+        return result;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerTypeSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerTypeSerializer.java
new file mode 100644
index 0000000..a77a4f7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedGenericWithSerializerTypeSerializer.java
@@ -0,0 +1,27 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+public class AnnotatedGenericWithSerializerTypeSerializer implements JsonbSerializer<AnnotatedGenericWithSerializerType<?>> {
+
+    @Override
+    public void serialize(AnnotatedGenericWithSerializerType<?> obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        ctx.serialize("generic", obj.value, generator);
+        generator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerType.java b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerType.java
new file mode 100644
index 0000000..bfa2ed6
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerType.java
@@ -0,0 +1,26 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbTypeSerializer(AnnotatedWithSerializerTypeSerializer.class)
+@JsonbTypeDeserializer(AnnotatedWithSerializerTypeDeserializer.class)
+public class AnnotatedWithSerializerType {
+
+    public String value;
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeDeserializer.java
new file mode 100644
index 0000000..a006d99
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeDeserializer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class AnnotatedWithSerializerTypeDeserializer implements JsonbDeserializer<AnnotatedWithSerializerType> {
+    /**
+     * Deserialize an object from JSON.
+     * Cursor of JsonParser is at START_OBJECT.
+     *
+     * @param parser Json parser
+     * @param ctx    Deserialization context
+     * @param rtType type of returned object
+     * @return deserialized instance
+     */
+    @Override
+    public AnnotatedWithSerializerType deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        AnnotatedWithSerializerType result = new AnnotatedWithSerializerType();
+        parser.next();
+        parser.next();
+        result.value = parser.getString();
+        return result;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeSerializer.java
new file mode 100644
index 0000000..61d566c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeSerializer.java
@@ -0,0 +1,30 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class AnnotatedWithSerializerTypeSerializer implements JsonbSerializer<AnnotatedWithSerializerType> {
+
+    @Override
+    public void serialize(AnnotatedWithSerializerType obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.write("valueField", "replaced value");
+        generator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeSerializerOverride.java b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeSerializerOverride.java
new file mode 100644
index 0000000..e865e4f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/AnnotatedWithSerializerTypeSerializerOverride.java
@@ -0,0 +1,30 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class AnnotatedWithSerializerTypeSerializerOverride implements JsonbSerializer<AnnotatedWithSerializerType> {
+
+    @Override
+    public void serialize(AnnotatedWithSerializerType obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.write("valueField", "overridden value");
+        generator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Author.java b/src/test/java/org/eclipse/yasson/serializers/model/Author.java
new file mode 100644
index 0000000..224f946
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/Author.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+
+public class Author {
+
+    public static final class FirstNameSerializer implements JsonbSerializer<String> {
+
+        @Override
+        public void serialize(String obj, JsonGenerator generator, SerializationContext ctx) {
+            generator.write(obj.substring(0, 1));
+        }
+    }
+
+    public static final class FirstNameDeserializer implements JsonbDeserializer<String> {
+        @Override
+        public String deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+            return "John";
+        }
+    }
+
+
+    @JsonbTypeSerializer(FirstNameSerializer.class)
+    @JsonbTypeDeserializer(FirstNameDeserializer.class)
+    private String firstName;
+
+    private String lastName;
+
+    public Author() {
+    }
+
+    public Author(String firstName, String lastName) {
+        this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Box.java b/src/test/java/org/eclipse/yasson/serializers/model/Box.java
new file mode 100644
index 0000000..121b926
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/Box.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class Box {
+
+    public String boxStr;
+
+    public Crate crate;
+
+    public String secondBoxStr;
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/BoxWithAnnotations.java b/src/test/java/org/eclipse/yasson/serializers/model/BoxWithAnnotations.java
new file mode 100644
index 0000000..3604d21
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/BoxWithAnnotations.java
@@ -0,0 +1,31 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class BoxWithAnnotations {
+
+    public String boxStr;
+
+    @JsonbTypeSerializer(CrateSerializerWithConversion.class)
+    @JsonbTypeDeserializer(CrateDeserializer.class)
+    public Crate crate;
+
+
+    public String secondBoxStr;
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Containee.java b/src/test/java/org/eclipse/yasson/serializers/model/Containee.java
new file mode 100644
index 0000000..ada2d46
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/Containee.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import java.util.Objects;
+
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+
+@JsonbTypeDeserializer(ContaineeDeserializer.class)
+@JsonbTypeSerializer(ContaineeSerializer.class)
+public class Containee {
+    final String key;
+    final String value;
+
+    public Containee(String key, String value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Containee containee = (Containee) o;
+        return Objects.equals(key, containee.key) && Objects.equals(value, containee.value);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(key, value);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/ContaineeDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/ContaineeDeserializer.java
new file mode 100644
index 0000000..2327380
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/ContaineeDeserializer.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import java.lang.reflect.Type;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+
+public class ContaineeDeserializer implements JsonbDeserializer<Containee> {
+
+    @Override
+    public Containee deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        String key = null;
+        String value = "";
+        while (parser.hasNext()) {
+            var event = parser.next();
+            if (event == JsonParser.Event.KEY_NAME && parser.getString().equals("key")) {
+                parser.next(); // move to VALUE
+                key = parser.getString();
+            } else if (event == JsonParser.Event.KEY_NAME && parser.getString().equals("value")) {
+                parser.next(); // move to VALUE
+                value = parser.getString();
+            }
+        }
+        assert key != null;
+        return new Containee(key, value);
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/ContaineeSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/ContaineeSerializer.java
new file mode 100644
index 0000000..152e9ab
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/ContaineeSerializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+public class ContaineeSerializer implements JsonbSerializer<Containee> {
+
+    @Override
+    public void serialize(Containee obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.write("key", obj.key);
+        generator.write("value", obj.value);
+        generator.writeEnd();
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Container.java b/src/test/java/org/eclipse/yasson/serializers/model/Container.java
new file mode 100644
index 0000000..8c732fb
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/Container.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class Container {
+
+    private List<Containee> containees;
+
+    public Container() {
+        containees = new ArrayList<>();
+    }
+
+    public Container(List<Containee> containees) {
+        this.containees = containees;
+    }
+
+    public void setContainees(List<Containee> containees) {
+        this.containees = containees;
+    }
+
+    public List<Containee> getContainees() {
+        return containees;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Container container = (Container) o;
+        return Objects.equals(containees, container.containees);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(containees);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Crate.java b/src/test/java/org/eclipse/yasson/serializers/model/Crate.java
new file mode 100644
index 0000000..5cf67da
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/Crate.java
@@ -0,0 +1,45 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.bind.annotation.JsonbProperty;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class Crate {
+
+    @JsonbProperty("crate_str")
+    public String crateStr;
+
+    public CrateInner crateInner;
+
+    public List<CrateInner> crateInnerList;
+
+    public BigDecimal crateBigDec;
+
+    @JsonbDateFormat("dd.MM.yyy ^ HH:mm:ss")
+    public Date date;
+
+    public AnnotatedWithSerializerType annotatedType;
+
+    public AnnotatedGenericWithSerializerType<Crate> annotatedGenericType;
+
+    @JsonbTypeSerializer(AnnotatedWithSerializerTypeSerializerOverride.class)
+    public AnnotatedWithSerializerType annotatedTypeOverriddenOnProperty;
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/CrateDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/CrateDeserializer.java
new file mode 100644
index 0000000..b8a80fc
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/CrateDeserializer.java
@@ -0,0 +1,58 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import org.eclipse.yasson.TestTypeToken;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+
+
+public class CrateDeserializer implements JsonbDeserializer<Crate> {
+
+    @Override
+    public Crate deserialize(JsonParser jsonParser, DeserializationContext ctx, Type rtType) {
+        Crate crate = new Crate();
+        crate.crateStr = "abc";
+        crate.crateBigDec = new BigDecimal("123");
+
+        while (jsonParser.hasNext()) {
+            JsonParser.Event next = jsonParser.next();
+            if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("crateInner")) {
+                //invokes JSONB processing for a CrateInner as a root type with "shared" instance of JsonParser
+                crate.crateInner = ctx.deserialize(CrateInner.class, jsonParser);
+                continue;
+            }
+            if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("crateInnerList")) {
+                //invokes JSONB processing for a CrateInner as a root type with "shared" instance of JsonParser
+                crate.crateInnerList = ctx.deserialize(new TestTypeToken<ArrayList<CrateInner>>(){}.getType(), jsonParser);
+                continue;
+            }
+            if (next.equals(JsonParser.Event.KEY_NAME) && jsonParser.getString().equals("date")) {
+                //move to value
+                jsonParser.next();
+                //don't have context of processing here, no annotation customizations applied.
+//                crate.date = ctx.convertDefault(Date.class, jsonParser.getString());
+                ctx.deserialize(Date.class, jsonParser);
+            }
+
+        }
+
+        return crate;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/CrateDeserializerWithConversion.java b/src/test/java/org/eclipse/yasson/serializers/model/CrateDeserializerWithConversion.java
new file mode 100644
index 0000000..ae3cdcd
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/CrateDeserializerWithConversion.java
@@ -0,0 +1,39 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+import java.util.Date;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CrateDeserializerWithConversion implements JsonbDeserializer<Crate> {
+
+    @Override
+    public Crate deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        Crate result = new Crate();
+        while (parser.hasNext()) {
+            final JsonParser.Event next = parser.next();
+            if (next.equals(JsonParser.Event.KEY_NAME) && parser.getString().equals("date-converted")) {
+                parser.next();
+                result.date = ctx.deserialize(Date.class, parser);
+                break;
+            }
+        }
+        return result;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/CrateInner.java b/src/test/java/org/eclipse/yasson/serializers/model/CrateInner.java
new file mode 100644
index 0000000..b7101b7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/CrateInner.java
@@ -0,0 +1,32 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbDateFormat;
+import jakarta.json.bind.annotation.JsonbProperty;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @author Roman Grigoriadi
+ */
+@JsonbDateFormat("dd.MM.yyyy || HH:mm:ss")
+public class CrateInner {
+
+    @JsonbProperty("crate_inner_str")
+    public String crateInnerStr;
+
+    public BigDecimal crateInnerBigDec;
+
+    public Date date;
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/CrateJsonObjectDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/CrateJsonObjectDeserializer.java
new file mode 100644
index 0000000..0cb6311
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/CrateJsonObjectDeserializer.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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.JsonObject;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CrateJsonObjectDeserializer implements JsonbDeserializer<Crate> {
+
+    /**
+     * Deserialize an object from JSON.
+     * Cursor of JsonParser is at START_OBJECT.
+     *
+     * @param parser Json parser
+     * @param ctx    Deserialization context
+     * @param rtType type of returned object
+     * @return deserialized instance
+     */
+    @Override
+    public Crate deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        JsonObject crateJsonObject = ctx.deserialize(JsonObject.class, parser);
+        Crate crate = new Crate();
+        crate.crateInner = new CrateInner();
+        crate.crateInner.crateInnerStr = crateJsonObject.getJsonObject("crateInner").getString("crateInnerStr");
+        crate.crateInner.crateInnerBigDec = crateJsonObject.getJsonObject("crateInner").getJsonNumber("crateInnerBigDec").bigDecimalValue();
+        crate.crateStr = crateJsonObject.getString("crateStr");
+        crate.crateBigDec = crateJsonObject.getJsonNumber("crateBigDec").bigDecimalValue();
+        return crate;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/CrateSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/CrateSerializer.java
new file mode 100644
index 0000000..2164911
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/CrateSerializer.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import java.math.BigDecimal;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CrateSerializer implements JsonbSerializer<Crate> {
+
+    @Override
+    public void serialize(Crate obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.write("crateStr", "REPLACED crate str");
+        ctx.serialize("crateInner", obj.crateInner, generator);
+        ctx.serialize("crateInnerList", obj.crateInnerList, generator);
+        generator.write("crateBigDec", new BigDecimal("54321"));
+        generator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/CrateSerializerWithConversion.java b/src/test/java/org/eclipse/yasson/serializers/model/CrateSerializerWithConversion.java
new file mode 100644
index 0000000..fca3eed
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/CrateSerializerWithConversion.java
@@ -0,0 +1,34 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import java.math.BigDecimal;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class CrateSerializerWithConversion extends CrateSerializer {
+
+    @Override
+    public void serialize(Crate obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.write("crateStr", "REPLACED crate str");
+        ctx.serialize("crateInner", obj.crateInner, generator);
+        ctx.serialize("crateInnerList", obj.crateInnerList, generator);
+        generator.write("crateBigDec", new BigDecimal("54321"));
+        ctx.serialize("date-converted", obj.date, generator);
+        generator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/ExplicitJsonbSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/ExplicitJsonbSerializer.java
new file mode 100644
index 0000000..296dfd7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/ExplicitJsonbSerializer.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+
+/**
+ * Explicit serializer.
+ */
+public class ExplicitJsonbSerializer extends AbstractJsonbSerializer<Box> implements JsonbSerializer<Box> {
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/GenericPropertyPojo.java b/src/test/java/org/eclipse/yasson/serializers/model/GenericPropertyPojo.java
new file mode 100644
index 0000000..6ca05e7
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/GenericPropertyPojo.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+
+@JsonbTypeSerializer(GenericPropertyPojoSerializer.class)
+public class GenericPropertyPojo<T> {
+    private T property;
+
+    public T getProperty() {
+        return property;
+    }
+
+    public void setProperty(T property) {
+        this.property = property;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/GenericPropertyPojoSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/GenericPropertyPojoSerializer.java
new file mode 100644
index 0000000..919843d
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/GenericPropertyPojoSerializer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+public class GenericPropertyPojoSerializer implements JsonbSerializer<GenericPropertyPojo<Number>> {
+    @Override
+    public void serialize(GenericPropertyPojo<Number> obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.writeStartObject();
+        generator.writeKey("propertyByUserSerializer");
+        generator.write("Number value [" + obj.getProperty() + "]");
+        generator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/ImplicitJsonbSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/ImplicitJsonbSerializer.java
new file mode 100644
index 0000000..ffbe40b
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/ImplicitJsonbSerializer.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+/**
+ * Implicit serializer.
+ */
+public class ImplicitJsonbSerializer extends AbstractJsonbSerializer<Box> {
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/NumberDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/NumberDeserializer.java
new file mode 100644
index 0000000..c5bd8f2
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/NumberDeserializer.java
@@ -0,0 +1,25 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+
+public class NumberDeserializer implements JsonbDeserializer<Number> {
+    @Override
+    public Number deserialize(JsonParser parser, DeserializationContext ctx, Type rtType) {
+        return Integer.valueOf(parser.getString()) - 1;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/NumberSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/NumberSerializer.java
new file mode 100644
index 0000000..9ec4f1f
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/NumberSerializer.java
@@ -0,0 +1,24 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+public class NumberSerializer implements JsonbSerializer<Number> {
+    @Override
+    public void serialize(Number obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.write(Integer.valueOf(obj.intValue() + 1).toString());
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Pokemon.java b/src/test/java/org/eclipse/yasson/serializers/model/Pokemon.java
new file mode 100644
index 0000000..f7ac239
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/Pokemon.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import java.util.Objects;
+
+/**
+ * Serialization and de-serialization test model: {@code Pokemon}.
+ */
+public final class Pokemon {
+	public String name;
+	public String type;
+	public int cp;
+
+	public Pokemon() {
+		this.name = null;
+		this.type = null;
+		this.cp = -1;
+	}
+
+	public Pokemon(String name, String type, int cp) {
+		this.name = name;
+		this.type = type;
+		this.cp = cp;
+	}
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof Pokemon)) {
+            return false;
+        }
+        Pokemon pokemon = (Pokemon)other;
+        return cp == pokemon.cp
+                && Objects.equals(name, pokemon.name)
+                && Objects.equals(type, pokemon.type);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cp, name, type);
+    }
+
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/RecursiveDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/RecursiveDeserializer.java
new file mode 100644
index 0000000..6e91e92
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/RecursiveDeserializer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+
+/**
+ * Causes {@link StackOverflowError} if recursive calls of user components are not checked by runtime.
+ */
+public class RecursiveDeserializer implements JsonbDeserializer<Box> {
+    @Override
+    public Box deserialize(JsonParser jsonParser, DeserializationContext deserializationContext, Type type) {
+        return deserializationContext.deserialize(type, jsonParser);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/RecursiveSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/RecursiveSerializer.java
new file mode 100644
index 0000000..dca9734
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/RecursiveSerializer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * Causes {@link StackOverflowError} if recursive calls of user components are not checked by runtime.
+ */
+public class RecursiveSerializer implements JsonbSerializer<Box> {
+    @Override
+    public void serialize(Box box, JsonGenerator jsonGenerator, SerializationContext serializationContext) {
+        jsonGenerator.writeStartObject();
+        serializationContext.serialize("boxFieldName", box, jsonGenerator);
+        jsonGenerator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/SimpleAnnotatedSerializedArrayContainer.java b/src/test/java/org/eclipse/yasson/serializers/model/SimpleAnnotatedSerializedArrayContainer.java
new file mode 100644
index 0000000..2af8278
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/SimpleAnnotatedSerializedArrayContainer.java
@@ -0,0 +1,47 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/*
+ * $Id$
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+
+
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+import java.util.List;
+
+public class SimpleAnnotatedSerializedArrayContainer {
+    @JsonbTypeSerializer(SimpleContainerArraySerializer.class)
+    @JsonbTypeDeserializer(SimpleContainerArrayDeserializer.class)
+    private SimpleContainer[] arrayInstance;
+
+    private List<SimpleContainer> listInstance;
+
+    public SimpleContainer[] getArrayInstance() {
+        return arrayInstance;
+    }
+
+    public void setArrayInstance(SimpleContainer[] arrayInstance) {
+        this.arrayInstance = arrayInstance;
+    }
+
+    public List<SimpleContainer> getListInstance() {
+        return listInstance;
+    }
+
+    public void setListInstance(List<SimpleContainer> listInstance) {
+        this.listInstance = listInstance;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainer.java b/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainer.java
new file mode 100644
index 0000000..49d2d8c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainer.java
@@ -0,0 +1,38 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/*
+ * $Id$
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+
+
+public class SimpleContainer {
+    private String instance = "Test";
+
+    public SimpleContainer() {
+    }
+
+    public SimpleContainer(String instance) {
+        this.instance = instance;
+    }
+
+    public String getInstance() {
+        return instance;
+    }
+
+    public void setInstance(String instance) {
+        this.instance = instance;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainerArrayDeserializer.java b/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainerArrayDeserializer.java
new file mode 100644
index 0000000..04ecebd
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainerArrayDeserializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/*
+ * $Id$
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SimpleContainerArrayDeserializer implements JsonbDeserializer<SimpleContainer[]> {
+    public SimpleContainer[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext, Type type) {
+        List<SimpleContainer> containers = new ArrayList<>();
+
+        while (jsonParser.hasNext()) {
+            JsonParser.Event event = jsonParser.next();
+            if (event == JsonParser.Event.START_OBJECT) {
+                containers.add(deserializationContext.deserialize(new SimpleContainer(){}.getClass().getGenericSuperclass(), jsonParser));
+            }
+            if (event == JsonParser.Event.END_OBJECT) {
+                break;
+            }
+        }
+
+        return containers.toArray(new SimpleContainer[0]);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainerArraySerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainerArraySerializer.java
new file mode 100644
index 0000000..dc0704c
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/SimpleContainerArraySerializer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017, 2022 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+/*
+ * $Id$
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+public class SimpleContainerArraySerializer implements JsonbSerializer<SimpleContainer[]> {
+    @Override
+    public void serialize(SimpleContainer[] containers, JsonGenerator jsonGenerator, SerializationContext serializationContext) {
+        jsonGenerator.writeStartArray();
+        for (SimpleContainer container : containers) {
+            serializationContext.serialize(container, jsonGenerator);
+        }
+        jsonGenerator.writeEnd();
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/StringPaddingSerializer.java b/src/test/java/org/eclipse/yasson/serializers/model/StringPaddingSerializer.java
new file mode 100644
index 0000000..daa74c5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/StringPaddingSerializer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class StringPaddingSerializer implements JsonbSerializer<String> {
+    @Override
+    public void serialize(String obj, JsonGenerator generator, SerializationContext ctx) {
+        generator.write("   "+obj);
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/StringWrapper.java b/src/test/java/org/eclipse/yasson/serializers/model/StringWrapper.java
new file mode 100644
index 0000000..e1a2cc9
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/StringWrapper.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+
+/**
+ * @author Roman Grigoriadi
+ */
+public class StringWrapper {
+
+    @JsonbTypeSerializer(StringPaddingSerializer.class)
+    public String strField;
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/SupertypeSerializerPojo.java b/src/test/java/org/eclipse/yasson/serializers/model/SupertypeSerializerPojo.java
new file mode 100644
index 0000000..0feadb5
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/SupertypeSerializerPojo.java
@@ -0,0 +1,43 @@
+/*
+ * 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+
+public class SupertypeSerializerPojo {
+
+
+    @JsonbTypeSerializer(NumberSerializer.class)
+    @JsonbTypeDeserializer(NumberDeserializer.class)
+    private Integer numberInteger;
+
+    //Serializers bound with JsonbConfig for this one.
+    private Integer anotherNumberInteger;
+
+    public Integer getNumberInteger() {
+        return numberInteger;
+    }
+
+    public void setNumberInteger(Integer numberInteger) {
+        this.numberInteger = numberInteger;
+    }
+
+    public Integer getAnotherNumberInteger() {
+        return anotherNumberInteger;
+    }
+
+    public void setAnotherNumberInteger(Integer anotherNumberInteger) {
+        this.anotherNumberInteger = anotherNumberInteger;
+    }
+}
diff --git a/src/test/java/org/eclipse/yasson/serializers/model/Trainer.java b/src/test/java/org/eclipse/yasson/serializers/model/Trainer.java
new file mode 100644
index 0000000..c841ff8
--- /dev/null
+++ b/src/test/java/org/eclipse/yasson/serializers/model/Trainer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019, 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,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.eclipse.yasson.serializers.model;
+
+import java.util.Objects;
+
+/**
+ * Serialization and de-serialization test model: pokemon {@code Trainer}.
+ */
+public final class Trainer {
+    public String name;
+    public int age;
+
+    public Trainer() {
+        this.name = null;
+        this.age = -1;
+    }
+
+    public Trainer(String name, int age) {
+        this.name = name;
+        this.age = age;
+	}
+
+	@Override
+	public boolean equals(Object other) {
+	    if (this == other) {
+	        return true;
+	    }
+	    if (!(other instanceof Trainer)) {
+	        return false;
+	    }
+	    Trainer trainer = (Trainer)other;
+	    return age == trainer.age && Objects.equals(name, trainer.name);
+	}
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(age, name);
+    }
+
+}
diff --git a/src/test/resources/META-INF/beans.xml b/src/test/resources/META-INF/beans.xml
new file mode 100644
index 0000000..49be503
--- /dev/null
+++ b/src/test/resources/META-INF/beans.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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,
+    or the Eclipse Distribution License v. 1.0 which is available at
+    http://www.eclipse.org/org/documents/edl-v10.php.
+
+    SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+-->
+
+<beans xmlns="http://java.sun.com/xml/ns/javaee" />
diff --git a/src/test/resources/jndi.properties b/src/test/resources/jndi.properties
new file mode 100644
index 0000000..13065b4
--- /dev/null
+++ b/src/test/resources/jndi.properties
@@ -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 Public License v. 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+java.naming.factory.initial=org.eclipse.yasson.internal.cdi.MockJndiContextFactory
diff --git a/src/test/resources/logging.properties b/src/test/resources/logging.properties
new file mode 100644
index 0000000..1e9ebe4
--- /dev/null
+++ b/src/test/resources/logging.properties
@@ -0,0 +1,17 @@
+#
+# 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,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+handlers=java.util.logging.ConsoleHandler
+.level=INFO
+
+java.util.logging.ConsoleHandler.level=INFO
+java.util.logging.ConsoleHandler.formatter=org.eclipse.yasson.logger.JsonbLoggerFormatter
diff --git a/src/test/resources/test.policy b/src/test/resources/test.policy
new file mode 100644
index 0000000..e247b53
--- /dev/null
+++ b/src/test/resources/test.policy
@@ -0,0 +1,17 @@
+grant {
+      //needed to be able to read all files including META-INF entries
+      permission "java.io.FilePermission" "<<ALL FILES>>", "read";
+
+      permission "java.lang.reflect.ReflectPermission" "suppressAccessChecks";
+      permission "java.lang.RuntimePermission" "accessDeclaredMembers";
+
+      permission "java.lang.RuntimePermission" "createSecurityManager";
+      permission java.lang.RuntimePermission "setSecurityManager";
+      permission "java.lang.RuntimePermission" "getProtectionDomain";
+      permission "java.lang.RuntimePermission" "getClassLoader";
+      permission "java.util.PropertyPermission" "*", "write";
+
+      permission "java.util.PropertyPermission" "jsonb.creator-parameters-required", "read";
+      permission "java.util.PropertyPermission" "yasson.time-in-millis-as-a-string", "read";
+      permission "java.util.PropertyPermission" "jakarta.json.provider", "read";
+};
\ No newline at end of file
diff --git a/src/test/resources/yasson-messages_cs.properties b/src/test/resources/yasson-messages_cs.properties
new file mode 100644
index 0000000..313880b
--- /dev/null
+++ b/src/test/resources/yasson-messages_cs.properties
@@ -0,0 +1,16 @@
+#
+# 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,
+# or the Eclipse Distribution License v. 1.0 which is available at
+# http://www.eclipse.org/org/documents/edl-v10.php.
+#
+# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+#
+
+processFromJson = Zpracovávám třídu: {0} z jsonu za použití convertoru: {1}
+processToJson = Zpracovávám třídu: {0} do jsonu za použití convertoru: {1}
+
+diacriticsTest=p\u0159\u00edli\u0161,\u017elu\u0165ou\u010dk\u00fd,k\u016f\u0148,\u00fap\u011bl,\u010f\u00e1belsk\u00e9,\u00f3dy
diff --git a/yasson-jmh/.gitignore b/yasson-jmh/.gitignore
new file mode 100644
index 0000000..2f7896d
--- /dev/null
+++ b/yasson-jmh/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/yasson-jmh/README.md b/yasson-jmh/README.md
new file mode 100644
index 0000000..f5005a3
--- /dev/null
+++ b/yasson-jmh/README.md
@@ -0,0 +1,24 @@
+#JMH performance tests for Yasson.
+
+Binaries for performance testing should never be uploaded into Maven repository.
+
+Use of JMH dependency is approved for testing purposes only as "Works with" CQ type, see:
+
+[jmh-core CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=18908),  
+[jmh-generator-annprocess CQ](https://dev.eclipse.org/ipzilla/show_bug.cgi?id=18909)
+
+To run tests:
+ - build it with maven
+ - run from commandline
+
+For all JMH options available:
+```
+java -jar target/yasson-jmh.jar -h
+```
+
+Run examples:
+```
+java -jar target/yasson-jmh.jar
+java -jar target/yasson-jmh.jar -i 3 -t 5 -f 3 -prof stack
+java -jar target/yasson-jmh.jar ScalarDataTest.testDeserialize
+```
\ No newline at end of file
diff --git a/yasson-jmh/pom.xml b/yasson-jmh/pom.xml
new file mode 100644
index 0000000..dcdd303
--- /dev/null
+++ b/yasson-jmh/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.eclipse.yasson</groupId>
+    <artifactId>yasson-jmh</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <description>This is a performance testing project for Yasson. It leverages a JMH technology.
+        See https://openjdk.java.net/projects/code-tools/jmh/.
+    </description>
+
+    <properties>
+        <jmh.version>1.21</jmh.version>
+        <yasson.version>2.0.2-SNAPSHOT</yasson.version>
+    </properties>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-core</artifactId>
+            <version>${jmh.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.openjdk.jmh</groupId>
+            <artifactId>jmh-generator-annprocess</artifactId>
+            <version>${jmh.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse</groupId>
+            <artifactId>yasson</artifactId>
+            <version>${yasson.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Don't upload any files from these project. JMH dependencies are approved only for testing purposes. -->
+            <plugin>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>11</source>
+                    <target>11</target>
+                </configuration>
+            </plugin>
+            <!--run `java -jar yasson-jmh.jar -h` for help -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.2.1</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <finalName>yasson-jmh</finalName>
+                            <transformers>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.openjdk.jmh.Main</mainClass>
+                                </transformer>
+                            </transformers>
+                            <filters>
+                                <filter>
+                                    <!--
+                                        Shading signed JARs will fail without this.
+                                        http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar
+                                    -->
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/CollectionsTest.java b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/CollectionsTest.java
new file mode 100644
index 0000000..5eb0b34
--- /dev/null
+++ b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/CollectionsTest.java
@@ -0,0 +1,51 @@
+package org.eclipse.yasson.jmh;
+
+import org.eclipse.yasson.jmh.model.CollectionsData;
+import org.eclipse.yasson.jmh.model.ScalarData;
+import org.openjdk.jmh.annotations.*;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for collections processing performance.
+ */
+@BenchmarkMode(Mode.Throughput)
+@Timeout(time = 20)
+@State(Scope.Benchmark)
+@Warmup(iterations = 3)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+public class CollectionsTest {
+
+    private CollectionsData data;
+
+    private Jsonb jsonb;
+
+    private String json;
+
+    @Setup(Level.Trial)
+    public void setUp() {
+        data = new CollectionsData();
+        data.setListData(new ArrayList<>());
+        data.setSetData(new HashSet<>());
+        for (int i = 0; i < 50; i++) {
+            data.getListData().add(new ScalarData("strValue" + i, i));
+            data.getSetData().add(new ScalarData("strValue" + i, i));
+        }
+        jsonb = JsonbBuilder.create();
+        json = "{\"listData\":[{\"integerValue\":0,\"stringValue\":\"strValue0\"},{\"integerValue\":1,\"stringValue\":\"strValue1\"},{\"integerValue\":2,\"stringValue\":\"strValue2\"},{\"integerValue\":3,\"stringValue\":\"strValue3\"},{\"integerValue\":4,\"stringValue\":\"strValue4\"},{\"integerValue\":5,\"stringValue\":\"strValue5\"},{\"integerValue\":6,\"stringValue\":\"strValue6\"},{\"integerValue\":7,\"stringValue\":\"strValue7\"},{\"integerValue\":8,\"stringValue\":\"strValue8\"},{\"integerValue\":9,\"stringValue\":\"strValue9\"},{\"integerValue\":10,\"stringValue\":\"strValue10\"},{\"integerValue\":11,\"stringValue\":\"strValue11\"},{\"integerValue\":12,\"stringValue\":\"strValue12\"},{\"integerValue\":13,\"stringValue\":\"strValue13\"},{\"integerValue\":14,\"stringValue\":\"strValue14\"},{\"integerValue\":15,\"stringValue\":\"strValue15\"},{\"integerValue\":16,\"stringValue\":\"strValue16\"},{\"integerValue\":17,\"stringValue\":\"strValue17\"},{\"integerValue\":18,\"stringValue\":\"strValue18\"},{\"integerValue\":19,\"stringValue\":\"strValue19\"},{\"integerValue\":20,\"stringValue\":\"strValue20\"},{\"integerValue\":21,\"stringValue\":\"strValue21\"},{\"integerValue\":22,\"stringValue\":\"strValue22\"},{\"integerValue\":23,\"stringValue\":\"strValue23\"},{\"integerValue\":24,\"stringValue\":\"strValue24\"},{\"integerValue\":25,\"stringValue\":\"strValue25\"},{\"integerValue\":26,\"stringValue\":\"strValue26\"},{\"integerValue\":27,\"stringValue\":\"strValue27\"},{\"integerValue\":28,\"stringValue\":\"strValue28\"},{\"integerValue\":29,\"stringValue\":\"strValue29\"},{\"integerValue\":30,\"stringValue\":\"strValue30\"},{\"integerValue\":31,\"stringValue\":\"strValue31\"},{\"integerValue\":32,\"stringValue\":\"strValue32\"},{\"integerValue\":33,\"stringValue\":\"strValue33\"},{\"integerValue\":34,\"stringValue\":\"strValue34\"},{\"integerValue\":35,\"stringValue\":\"strValue35\"},{\"integerValue\":36,\"stringValue\":\"strValue36\"},{\"integerValue\":37,\"stringValue\":\"strValue37\"},{\"integerValue\":38,\"stringValue\":\"strValue38\"},{\"integerValue\":39,\"stringValue\":\"strValue39\"},{\"integerValue\":40,\"stringValue\":\"strValue40\"},{\"integerValue\":41,\"stringValue\":\"strValue41\"},{\"integerValue\":42,\"stringValue\":\"strValue42\"},{\"integerValue\":43,\"stringValue\":\"strValue43\"},{\"integerValue\":44,\"stringValue\":\"strValue44\"},{\"integerValue\":45,\"stringValue\":\"strValue45\"},{\"integerValue\":46,\"stringValue\":\"strValue46\"},{\"integerValue\":47,\"stringValue\":\"strValue47\"},{\"integerValue\":48,\"stringValue\":\"strValue48\"},{\"integerValue\":49,\"stringValue\":\"strValue49\"}],\"setData\":[{\"integerValue\":36,\"stringValue\":\"strValue36\"},{\"integerValue\":21,\"stringValue\":\"strValue21\"},{\"integerValue\":38,\"stringValue\":\"strValue38\"},{\"integerValue\":25,\"stringValue\":\"strValue25\"},{\"integerValue\":32,\"stringValue\":\"strValue32\"},{\"integerValue\":9,\"stringValue\":\"strValue9\"},{\"integerValue\":42,\"stringValue\":\"strValue42\"},{\"integerValue\":35,\"stringValue\":\"strValue35\"},{\"integerValue\":6,\"stringValue\":\"strValue6\"},{\"integerValue\":43,\"stringValue\":\"strValue43\"},{\"integerValue\":37,\"stringValue\":\"strValue37\"},{\"integerValue\":27,\"stringValue\":\"strValue27\"},{\"integerValue\":2,\"stringValue\":\"strValue2\"},{\"integerValue\":1,\"stringValue\":\"strValue1\"},{\"integerValue\":24,\"stringValue\":\"strValue24\"},{\"integerValue\":28,\"stringValue\":\"strValue28\"},{\"integerValue\":29,\"stringValue\":\"strValue29\"},{\"integerValue\":23,\"stringValue\":\"strValue23\"},{\"integerValue\":12,\"stringValue\":\"strValue12\"},{\"integerValue\":22,\"stringValue\":\"strValue22\"},{\"integerValue\":49,\"stringValue\":\"strValue49\"},{\"integerValue\":5,\"stringValue\":\"strValue5\"},{\"integerValue\":44,\"stringValue\":\"strValue44\"},{\"integerValue\":41,\"stringValue\":\"strValue41\"},{\"integerValue\":45,\"stringValue\":\"strValue45\"},{\"integerValue\":0,\"stringValue\":\"strValue0\"},{\"integerValue\":18,\"stringValue\":\"strValue18\"},{\"integerValue\":8,\"stringValue\":\"strValue8\"},{\"integerValue\":10,\"stringValue\":\"strValue10\"},{\"integerValue\":33,\"stringValue\":\"strValue33\"},{\"integerValue\":30,\"stringValue\":\"strValue30\"},{\"integerValue\":13,\"stringValue\":\"strValue13\"},{\"integerValue\":16,\"stringValue\":\"strValue16\"},{\"integerValue\":48,\"stringValue\":\"strValue48\"},{\"integerValue\":3,\"stringValue\":\"strValue3\"},{\"integerValue\":31,\"stringValue\":\"strValue31\"},{\"integerValue\":40,\"stringValue\":\"strValue40\"},{\"integerValue\":15,\"stringValue\":\"strValue15\"},{\"integerValue\":39,\"stringValue\":\"strValue39\"},{\"integerValue\":14,\"stringValue\":\"strValue14\"},{\"integerValue\":46,\"stringValue\":\"strValue46\"},{\"integerValue\":34,\"stringValue\":\"strValue34\"},{\"integerValue\":17,\"stringValue\":\"strValue17\"},{\"integerValue\":7,\"stringValue\":\"strValue7\"},{\"integerValue\":20,\"stringValue\":\"strValue20\"},{\"integerValue\":4,\"stringValue\":\"strValue4\"},{\"integerValue\":11,\"stringValue\":\"strValue11\"},{\"integerValue\":19,\"stringValue\":\"strValue19\"},{\"integerValue\":47,\"stringValue\":\"strValue47\"},{\"integerValue\":26,\"stringValue\":\"strValue26\"}]}";
+    }
+
+    @Benchmark
+    public String testSerialize() {
+        return jsonb.toJson(data);
+    }
+
+    @Benchmark
+    public CollectionsData testDeserialize() {
+        return jsonb.fromJson(json, CollectionsData.class);
+    }
+}
diff --git a/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/ScalarDataTest.java b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/ScalarDataTest.java
new file mode 100644
index 0000000..2f7d68c
--- /dev/null
+++ b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/ScalarDataTest.java
@@ -0,0 +1,44 @@
+package org.eclipse.yasson.jmh;
+
+import org.eclipse.yasson.jmh.model.ScalarData;
+import org.openjdk.jmh.annotations.*;
+
+import jakarta.json.bind.Jsonb;
+import jakarta.json.bind.JsonbBuilder;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Tests for simple data, which does not have inner object references or collections.
+ */
+@BenchmarkMode(Mode.Throughput)
+@Timeout(time = 20)
+@State(Scope.Benchmark)
+@Warmup(iterations = 3)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+public class ScalarDataTest {
+
+    private Jsonb jsonb;
+
+    private ScalarData data;
+
+    private String json;
+
+    @Setup(Level.Trial)
+    public void setUp() {
+        this.jsonb = JsonbBuilder.create();
+        this.data = new ScalarData();
+        this.json = "{\"integerValue\":10,\"stringValue\":\"Short string\"}";
+    }
+
+    @Benchmark
+    public String testSerialize() {
+        return jsonb.toJson(data);
+    }
+
+    @Benchmark
+    public ScalarData testDeserialize() {
+        return jsonb.fromJson(json, ScalarData.class);
+    }
+
+}
diff --git a/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/TenPropertySerializationTest.java b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/TenPropertySerializationTest.java
new file mode 100644
index 0000000..32ebdf7
--- /dev/null
+++ b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/TenPropertySerializationTest.java
@@ -0,0 +1,33 @@
+package org.eclipse.yasson.jmh;

+

+import org.eclipse.yasson.jmh.model.TenPropertyData;

+import org.openjdk.jmh.annotations.*;

+

+import jakarta.json.bind.*;

+import java.util.List;

+import java.util.concurrent.TimeUnit;

+

+/**

+ * Test for serializing a pojo with 10 properties

+ */

+@BenchmarkMode(Mode.Throughput)

+@Timeout(time = 20)

+@State(Scope.Benchmark)

+@Warmup(iterations = 3)

+@OutputTimeUnit(TimeUnit.MILLISECONDS)

+public class TenPropertySerializationTest {

+	private Jsonb json;

+	private TenPropertyData testData;

+	

+	@Setup

+	public void setup() {

+		json = JsonbBuilder.create();

+		testData = new TenPropertyData("prop1", "prop2", 42, 3.14, new String[] {"props", "propsss", "psssss"}, 

+									   List.of(5, 1, 3), "prop7", 800, new int[] {5555, 6666, 7777, 8888}, Long.MAX_VALUE);

+	}

+	

+	@Benchmark

+	public String testSerialize() {

+		return json.toJson(testData);

+	}

+}
\ No newline at end of file
diff --git a/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/CollectionsData.java b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/CollectionsData.java
new file mode 100644
index 0000000..edcf205
--- /dev/null
+++ b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/CollectionsData.java
@@ -0,0 +1,27 @@
+package org.eclipse.yasson.jmh.model;
+
+import java.util.List;
+import java.util.Set;
+
+public class CollectionsData {
+
+    private List<ScalarData> listData;
+
+    private Set<ScalarData> setData;
+
+    public List<ScalarData> getListData() {
+        return listData;
+    }
+
+    public void setListData(List<ScalarData> listData) {
+        this.listData = listData;
+    }
+
+    public Set<ScalarData> getSetData() {
+        return setData;
+    }
+
+    public void setSetData(Set<ScalarData> setData) {
+        this.setData = setData;
+    }
+}
diff --git a/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/ScalarData.java b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/ScalarData.java
new file mode 100644
index 0000000..7b5167c
--- /dev/null
+++ b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/ScalarData.java
@@ -0,0 +1,34 @@
+package org.eclipse.yasson.jmh.model;
+
+public class ScalarData {
+
+    private String stringValue;
+
+    private Integer integerValue;
+
+    public ScalarData(String stringValue, Integer integerValue) {
+        this.stringValue = stringValue;
+        this.integerValue = integerValue;
+    }
+
+    public ScalarData() {
+        this.stringValue = "Short string";
+        this.integerValue = 10;
+    }
+
+    public String getStringValue() {
+        return stringValue;
+    }
+
+    public void setStringValue(String stringValue) {
+        this.stringValue = stringValue;
+    }
+
+    public Integer getIntegerValue() {
+        return integerValue;
+    }
+
+    public void setIntegerValue(Integer integerValue) {
+        this.integerValue = integerValue;
+    }
+}
diff --git a/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/TenPropertyData.java b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/TenPropertyData.java
new file mode 100644
index 0000000..23f93f1
--- /dev/null
+++ b/yasson-jmh/src/main/java/org/eclipse/yasson/jmh/model/TenPropertyData.java
@@ -0,0 +1,70 @@
+package org.eclipse.yasson.jmh.model;

+

+import java.util.*;

+

+public class TenPropertyData{

+	private final String prop1;

+	private final String prop2;

+	private final int prop3;

+	private final double prop4;

+	private final String[] prop5;

+	private final List<Integer> prop6;

+	private final String prop7;

+	private final int prop8;

+	private final int[] prop9;

+	private final long prop10;

+	

+	public TenPropertyData(String prop1, String prop2, int prop3, double prop4, String[] prop5, List<Integer> prop6,

+			String prop7, int prop8, int[] prop9, long prop10) {

+		this.prop1 = prop1;

+		this.prop2 = prop2;

+		this.prop3 = prop3;

+		this.prop4 = prop4;

+		this.prop5 = prop5;

+		this.prop6 = prop6;

+		this.prop7 = prop7;

+		this.prop8 = prop8;

+		this.prop9 = prop9;

+		this.prop10 = prop10;

+	}

+

+	public String getProp1() {

+		return prop1;

+	}

+

+	public String getProp2() {

+		return prop2;

+	}

+

+	public int getProp3() {

+		return prop3;

+	}

+

+	public double getProp4() {

+		return prop4;

+	}

+

+	public String[] getProp5() {

+		return prop5;

+	}

+

+	public List<Integer> getProp6() {

+		return prop6;

+	}

+

+	public String getProp7() {

+		return prop7;

+	}

+

+	public int getProp8() {

+		return prop8;

+	}

+

+	public int[] getProp9() {

+		return prop9;

+	}

+

+	public long getProp10() {

+		return prop10;

+	}

+}
\ No newline at end of file
diff --git a/yasson-tck/.gitignore b/yasson-tck/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/yasson-tck/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/yasson-tck/pom.xml b/yasson-tck/pom.xml
new file mode 100644
index 0000000..bd0d76e
--- /dev/null
+++ b/yasson-tck/pom.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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.eclipse</groupId>
+    <artifactId>yasson-tck</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+
+    <properties>
+        <jsonb.tck.version>3.0.0</jsonb.tck.version>
+        <yasson.version>3.0.4-SNAPSHOT</yasson.version>
+        <jakarta.json.bind.version>3.0.1</jakarta.json.bind.version>
+        <jakarta.json.version>2.1.3</jakarta.json.version>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <!-- TODO: Temporarily enable snapshot repository -->
+    <!-- This can be removed once an official release of jakarta.json.bind-tck is available -->
+    <repositories>
+        <repository>
+            <id>jakarta-snapshots</id>
+            <url>https://jakarta.oss.sonatype.org/content/repositories/staging/</url>
+        </repository>
+    </repositories>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.json.bind</groupId>
+            <artifactId>jakarta.json.bind-tck</artifactId>
+            <version>${jsonb.tck.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json.bind</groupId>
+            <artifactId>jakarta.json.bind-api</artifactId>
+            <version>${jakarta.json.bind.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>jakarta.json</groupId>
+            <artifactId>jakarta.json-api</artifactId>
+            <version>${jakarta.json.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse</groupId>
+            <artifactId>yasson</artifactId>
+            <version>${yasson.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.weld.se</groupId>
+            <artifactId>weld-se-core</artifactId>
+            <version>6.0.0.Beta1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.arquillian.junit5</groupId>
+            <artifactId>arquillian-junit5-container</artifactId>
+            <version>1.8.0.Final</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>3.6.1</version>
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>generate-test-sources</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>jakarta.json.bind</groupId>
+                                    <artifactId>jakarta.json.bind-api</artifactId>
+                                    <version>${jakarta.json.bind.version}</version>
+                                    <type>jar</type>
+                                    <overWrite>true</overWrite>
+                                    <outputDirectory>${project.build.directory}/signaturedirectory</outputDirectory>
+                                    <destFileName>jakarta.json.bind-api.jar</destFileName>
+                                </artifactItem>
+                                <artifactItem>
+                                    <groupId>jakarta.json</groupId>
+                                    <artifactId>jakarta.json-api</artifactId>
+                                    <version>${jakarta.json.version}</version>
+                                    <type>jar</type>
+                                    <overWrite>true</overWrite>
+                                    <outputDirectory>${project.build.directory}/signaturedirectory</outputDirectory>
+                                    <destFileName>jakarta.json-api.jar</destFileName>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.2.5</version>
+                <configuration>
+                    <trimStackTrace>false</trimStackTrace>
+                    <failIfNoTests>true</failIfNoTests>
+                    <dependenciesToScan>jakarta.json.bind:jakarta.json.bind-tck</dependenciesToScan>
+                    <systemPropertyVariables>
+                        <jimage.dir>${project.build.directory}/jdk-bundle</jimage.dir>
+                        <signature.sigTestClasspath>
+                            ${project.build.directory}/signaturedirectory/jakarta.json.bind-api.jar:${project.build.directory}/jdk-bundle/java.base:${project.build.directory}/jdk-bundle/java.rmi:${project.build.directory}/jdk-bundle/java.sql:${project.build.directory}/jdk-bundle/java.naming
+                        </signature.sigTestClasspath>
+                    </systemPropertyVariables>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-report-plugin</artifactId>
+                <version>3.2.5</version>
+                <executions>
+                    <execution>
+                        <id>post-unit-test</id>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>