You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2019/07/22 09:43:54 UTC

[sling-org-apache-sling-contentparser-json] branch master created (now 638ae05)

This is an automated email from the ASF dual-hosted git repository.

radu pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git.


      at 638ae05  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

This branch includes the following new commits:

     new ebbec84  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
     new f77d16d  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
     new e6ac640  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
     new e6239df  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
     new 37a15b4  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
     new e5d6e17  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
     new 65cd42c  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
     new 638ae05  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[sling-org-apache-sling-contentparser-json] 08/08: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git

commit 638ae05b6d82fa976411ff78857046095a0ea290
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Mon Jul 22 11:42:31 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * added Jenkinsfile and updated links in README
---
 Jenkinsfile | 20 ++++++++++++++++++++
 README.md   |  2 +-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..3882176
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,20 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+// please rebuild
+slingOsgiBundleBuild()
diff --git a/README.md b/README.md
index 1b07e37..86c0903 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ Apache Sling Content Parser for JSON
 This module is part of the [Apache Sling](https://sling.apache.org) project.
 
 The Apache Sling Content Parser for JSON provides support for parsing JSON files into Apache Sling resource trees, by implementing the 
-API provided by the [`org.apache.sling.contentparser.api`](https://github.com/apache/sling-whiteboard/tree/master/contentparser/org-apache-sling-contentparser-api) bundle.
+API provided by the [`org.apache.sling.contentparser.api`](https://github.com/apache/sling-org-apache-sling-contentparser-api) bundle.
 
 To obtain a reference to the JSON content parser just filter on the `ContentParser.SERVICE_PROPERTY_CONTENT_TYPE` service registration 
 property:


[sling-org-apache-sling-contentparser-json] 06/08: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git

commit e5d6e17f237177f906b5943290d0d4c837b04033
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Wed Jul 17 09:59:05 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * added README, CODE_OF_CONDUCT, CONTRIBUTING, LICENSE and .gitignore files
    to each module
---
 .gitignore         |  17 +++++
 CODE_OF_CONDUCT.md |  22 ++++++
 CONTRIBUTING.md    |  24 +++++++
 LICENSE            | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 README.md          |  14 ++++
 5 files changed, 279 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5b783ed
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+/target
+.idea
+.classpath
+.metadata
+.project
+.settings
+.externalToolBuilders
+maven-eclipse.xml
+*.swp
+*.iml
+*.ipr
+*.iws
+*.bak
+.vlt
+.DS_Store
+jcr.log
+atlassian-ide-plugin.xml
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..0fa18e5
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,22 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Apache Software Foundation Code of Conduct
+====
+
+Being an Apache project, Apache Sling adheres to the Apache Software Foundation's [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ac82a1a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,24 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Contributing
+====
+
+Thanks for choosing to contribute!
+
+You will find all the necessary details about how you can do this at https://sling.apache.org/contributing.html.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) 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. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0359394
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+Apache Sling Content Parser for JSON
+====
+This module is part of the [Apache Sling](https://sling.apache.org) project.
+
+The Apache Sling Content Parser for JSON provides support for parsing JSON files into Apache Sling resource trees, by implementing the 
+API provided by the [`org.apache.sling.contentparser.api`](https://github.com/apache/sling-whiteboard/tree/master/contentparser/org-apache-sling-contentparser-api) bundle.
+
+To obtain a reference to the JSON content parser just filter on the `ContentParser.SERVICE_PROPERTY_CONTENT_TYPE` service registration 
+property:
+
+```java
+    @Reference(target = "(" + ContentParser.SERVICE_PROPERTY_CONTENT_TYPE + "=" + ContentParser.JSON_CONTENT_TYPE + ")")
+    private ContentParser jsonParser;
+``` 


[sling-org-apache-sling-contentparser-json] 01/08: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git

commit ebbec8446d5a5efec62e5471bd0b91de610c7bbe
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Wed Jul 10 19:19:43 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * extracted API bundle
    * first attempt at implementing a separate JSON content parser
---
 pom.xml                                            | 112 +++++++++
 .../json/internal/JsonContentParser.java           | 212 ++++++++++++++++
 .../json/internal/JsonTicksConverter.java          | 106 ++++++++
 .../json/internal/JsonContentParserTest.java       | 186 ++++++++++++++
 .../contentparser/json/internal/TestUtils.java     |  60 +++++
 .../json/internal/mapsupport/ContentElement.java   |  52 ++++
 .../internal/mapsupport/ContentElementHandler.java |  69 ++++++
 .../internal/mapsupport/ContentElementImpl.java    |  68 +++++
 src/test/resources/content-test/content.json       | 274 +++++++++++++++++++++
 .../invalid-test/contentWithObjectList.json        |  14 ++
 src/test/resources/invalid-test/invalid.json       |   1 +
 11 files changed, 1154 insertions(+)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a33598b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>35</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.contentparser.json</artifactId>
+    <version>0.9.0-SNAPSHOT</version>
+
+    <name>Apache Sling Content Parser API</name>
+    <description>
+        Parser API Apache Sling Resource trees stored in files (e.g. JSON, FileVault XML, etc.).
+    </description>
+
+    <scm>
+        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-contentparser-json.git</url>
+      <tag>HEAD</tag>
+  </scm>
+
+    <build>
+        <plugins>
+            
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.contentparser.api</artifactId>
+            <version>0.9.0-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-json_1.0_spec</artifactId>
+            <version>1.0-alpha-1</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.6</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.8</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.framework</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>15.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.johnzon</groupId>
+            <artifactId>johnzon-core</artifactId>
+            <version>1.0.0</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java b/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java
new file mode 100644
index 0000000..16d47e0
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java
@@ -0,0 +1,212 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonNumber;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonReaderFactory;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.stream.JsonParsingException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.CharEncoding;
+import org.apache.sling.contentparser.api.ContentHandler;
+import org.apache.sling.contentparser.api.ContentParser;
+import org.apache.sling.contentparser.api.JsonParserFeature;
+import org.apache.sling.contentparser.api.ParseException;
+import org.apache.sling.contentparser.api.ParserHelper;
+import org.apache.sling.contentparser.api.ParserOptions;
+import org.osgi.service.component.annotations.Component;
+
+@Component(
+        property = {
+                ContentParser.SERVICE_PROPERTY_CONTENT_TYPE + "=" + ContentParser.JSON_CONTENT_TYPE
+        }
+)
+public class JsonContentParser implements ContentParser {
+
+    @Override
+    public void parse(ContentHandler handler, InputStream is, ParserOptions parserOptions) throws ParseException {
+        final boolean jsonQuoteTicks = parserOptions.getJsonParserFeatures().contains(JsonParserFeature.QUOTE_TICK);
+
+        /*
+         * Implementation note: This parser uses JsonReader instead of the (more memory-efficient)
+         * JsonParser Stream API because otherwise it would not be possible to report parent resources
+         * including all properties properly before their children.
+         */
+        final JsonReaderFactory jsonReaderFactory =
+                Json.createReaderFactory(
+                        parserOptions.getJsonParserFeatures().contains(JsonParserFeature.COMMENTS) ?
+                                new HashMap<String, Object>() {{
+                                    put("org.apache.johnzon.supports-comments", true);
+                                }} :
+                                Collections.emptyMap()
+                );
+        JsonObject jsonObject = jsonQuoteTicks ? toJsonObjectWithJsonTicks(jsonReaderFactory, is) : toJsonObject(jsonReaderFactory, is);
+        parse(handler, jsonObject, parserOptions, "/");
+    }
+
+    private JsonObject toJsonObject(JsonReaderFactory jsonReaderFactory, InputStream is) {
+        try (JsonReader reader = jsonReaderFactory.createReader(is)) {
+            return reader.readObject();
+        } catch (JsonParsingException ex) {
+            throw new ParseException("Error parsing JSON content: " + ex.getMessage(), ex);
+        }
+    }
+
+    private JsonObject toJsonObjectWithJsonTicks(JsonReaderFactory jsonReaderFactory, InputStream is) {
+        String jsonString;
+        try {
+            jsonString = IOUtils.toString(is, CharEncoding.UTF_8);
+        } catch (IOException ex) {
+            throw new ParseException("Error getting JSON string.", ex);
+        }
+
+        // convert ticks to double quotes
+        jsonString = JsonTicksConverter.tickToDoubleQuote(jsonString);
+
+        try (JsonReader reader = jsonReaderFactory.createReader(new StringReader(jsonString))) {
+            return reader.readObject();
+        } catch (JsonParsingException ex) {
+            throw new ParseException("Error parsing JSON content: " + ex.getMessage(), ex);
+        }
+    }
+
+    private void parse(ContentHandler handler, JsonObject object, ParserOptions parserOptions, String path) {
+        // parse JSON object
+        Map<String, Object> properties = new HashMap<>();
+        Map<String, JsonObject> children = new LinkedHashMap<>();
+        for (Map.Entry<String, JsonValue> entry : object.entrySet()) {
+            String childName = entry.getKey();
+            Object value = null;
+            boolean ignore = false;
+            try {
+                value = convertValue(parserOptions, entry.getValue());
+            } catch (ParseException ex) {
+                if (parserOptions.getIgnoreResourceNames().contains(childName) || parserOptions.getIgnorePropertyNames()
+                        .contains(removePrefixFromPropertyName(parserOptions.getRemovePropertyNamePrefixes(), childName))) {
+                    ignore = true;
+                } else {
+                    throw ex;
+                }
+            }
+            boolean isResource = (value instanceof JsonObject);
+            if (!ignore) {
+                if (isResource) {
+                    ignore = parserOptions.getIgnoreResourceNames().contains(childName);
+                } else {
+                    for (String prefix : parserOptions.getRemovePropertyNamePrefixes()) {
+                        if (childName.startsWith(prefix)) {
+                            childName = childName.substring(prefix.length());
+                            break;
+                        }
+
+                    }
+                    ignore = parserOptions.getIgnorePropertyNames().contains(childName);
+                }
+            }
+            if (!ignore) {
+                if (isResource) {
+                    children.put(childName, (JsonObject) value);
+                } else {
+                    properties.put(childName, value);
+                }
+            }
+        }
+        String defaultPrimaryType = parserOptions.getDefaultPrimaryType();
+        if (defaultPrimaryType != null) {
+            if (!properties.containsKey("jcr:primaryType")) {
+                properties.put("jcr:primaryType", defaultPrimaryType);
+            }
+        }
+
+        // report current JSON object
+        handler.resource(path, properties);
+
+        // parse and report children
+        for (Map.Entry<String, JsonObject> entry : children.entrySet()) {
+            String childPath = path.endsWith("/") ? path + entry.getKey() : path + "/" + entry.getKey();
+            parse(handler, entry.getValue(), parserOptions, childPath);
+        }
+    }
+
+    private Object convertValue(ParserOptions parserOptions, JsonValue value) {
+        switch (value.getValueType()) {
+            case STRING:
+                String stringValue = ((JsonString) value).getString();
+                if (parserOptions.isDetectCalendarValues()) {
+                    Calendar calendar = ParserHelper.parseDate(stringValue);
+                    if (calendar != null) {
+                        return calendar;
+                    }
+                }
+                return stringValue;
+            case NUMBER:
+                JsonNumber numberValue = (JsonNumber) value;
+                if (numberValue.isIntegral()) {
+                    return numberValue.longValue();
+                } else {
+                    return numberValue.bigDecimalValue();
+                }
+            case TRUE:
+                return true;
+            case FALSE:
+                return false;
+            case NULL:
+                return null;
+            case ARRAY:
+                JsonArray arrayValue = (JsonArray) value;
+                Object[] values = new Object[arrayValue.size()];
+                for (int i = 0; i < values.length; i++) {
+                    values[i] = convertValue(parserOptions, arrayValue.get(i));
+                }
+                return ParserHelper.convertSingleTypeArray(values);
+            case OBJECT:
+                return value;
+            default:
+                throw new ParseException("Unexpected JSON value type: " + value.getValueType());
+        }
+    }
+
+    private String removePrefixFromPropertyName(Set<String> prefixes, String propertyName) {
+        for (String prefix : prefixes) {
+            if (propertyName.startsWith(prefix)) {
+                return propertyName.substring(prefix.length());
+            }
+        }
+        return propertyName;
+    }
+
+
+}
diff --git a/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java b/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java
new file mode 100644
index 0000000..df0be76
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java
@@ -0,0 +1,106 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json.internal;
+
+/**
+ * Converts JSON with ticks to JSON with quotes.
+ * <p>Conversions:</p>
+ * <ul>
+ * <li>Converts ticks ' to " when used as quotation marks for names or string values</li>
+ * <li>Within names or string values quoted with ticks, ticks have to be escaped with <code>\'</code>.
+ *     This escaping sign is removed on the conversion, because in JSON ticks must not be escaped.</li>
+ * <li>Within names or string values quoted with ticks, double quotes may or may not be escaped.
+ *     After the conversion they are always escaped.</li>
+ * </ul>
+ */
+final class JsonTicksConverter {
+    
+    private JsonTicksConverter() {
+        // static methods only
+    }
+    
+    static String tickToDoubleQuote(final String input) {
+        final int len = input.length();
+        final StringBuilder output = new StringBuilder(len);
+        boolean quoted = false;
+        boolean tickQuoted = false;
+        boolean escaped = false;
+        boolean comment = false;
+        char lastChar = ' ';
+        for (int i = 0; i < len; i++) {
+            char in = input.charAt(i);
+            if (quoted || tickQuoted) {
+                if (escaped) {
+                    if (in != '\'') {
+                        output.append("\\");
+                    }
+                    if (in == '\\') {
+                        output.append("\\");
+                    }
+                    escaped = false;
+                }
+                else {
+                    if (in == '"') {
+                        if (quoted) {
+                            quoted = false;
+                        }
+                        else if (tickQuoted) {
+                            output.append("\\");
+                        }
+                    }
+                    else if (in == '\'') {
+                        if (tickQuoted) {
+                            in = '"';
+                            tickQuoted = false;
+                        }
+                    }
+                    else if (in == '\\') {
+                        escaped = true;
+                    }
+                }
+            }
+            else {
+                if (comment) {
+                    if (lastChar == '*' && in == '/') {
+                        comment = false;
+                    }
+                }
+                else {
+                    if (lastChar == '/' && in == '*') {
+                        comment = true;
+                    }
+                    else if (in == '\'') {
+                        in = '"';
+                        tickQuoted = true;
+                    }
+                    else if (in == '"') {
+                        quoted = true;
+                    }
+                }
+            }
+            if (in == '\\') {
+                continue;
+            }
+            output.append(in);
+            lastChar = in;
+        }
+        return output.toString();
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java b/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
new file mode 100644
index 0000000..3099ae8
--- /dev/null
+++ b/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
@@ -0,0 +1,186 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json.internal;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.sling.contentparser.api.ContentParser;
+import org.apache.sling.contentparser.api.JsonParserFeature;
+import org.apache.sling.contentparser.api.ParseException;
+import org.apache.sling.contentparser.api.ParserOptions;
+import org.apache.sling.contentparser.json.internal.mapsupport.ContentElement;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+import static junit.framework.TestCase.assertNull;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class JsonContentParserTest {
+
+    private File file;
+    private ContentParser contentParser;
+
+    @Before
+    public void setUp() {
+        file = new File("src/test/resources/content-test/content.json");
+        contentParser = new JsonContentParser();
+    }
+
+    @Test
+    public void testPageJcrPrimaryType() throws Exception {
+        ContentElement content = TestUtils.parse(contentParser, file);
+
+        assertEquals("app:Page", content.getProperties().get("jcr:primaryType"));
+    }
+
+    @Test
+    public void testDataTypes() throws Exception {
+        ContentElement content = TestUtils.parse(contentParser, file);
+
+        Map<String, Object> props = content.getChild("toolbar/profiles/jcr:content").getProperties();
+        assertEquals(true, props.get("hideInNav"));
+
+        assertEquals(1234567890123L, props.get("longProp"));
+        assertEquals(new BigDecimal("1.2345"), props.get("decimalProp"));
+        assertEquals(true, props.get("booleanProp"));
+
+        assertArrayEquals(new Long[]{1234567890123L, 55L}, (Long[]) props.get("longPropMulti"));
+        assertArrayEquals(new BigDecimal[]{new BigDecimal("1.2345"), new BigDecimal("1.1")}, (BigDecimal[]) props.get("decimalPropMulti"));
+        assertArrayEquals(new Boolean[]{true, false}, (Boolean[]) props.get("booleanPropMulti"));
+    }
+
+    @Test
+    public void testContentProperties() throws Exception {
+        ContentElement content = TestUtils.parse(contentParser, file);
+
+        Map<String, Object> props = content.getChild("jcr:content/header").getProperties();
+        assertEquals("/content/dam/sample/header.png", props.get("imageReference"));
+    }
+
+    @Test
+    public void testCalendar() throws Exception {
+        ContentElement content = TestUtils.parse(contentParser, new ParserOptions().detectCalendarValues(true), file);
+
+        Map<String, Object> props = content.getChild("jcr:content").getProperties();
+
+        Calendar calendar = (Calendar) props.get("app:lastModified");
+        assertNotNull(calendar);
+
+        calendar.setTimeZone(TimeZone.getTimeZone("GMT+2"));
+
+        assertEquals(2014, calendar.get(Calendar.YEAR));
+        assertEquals(4, calendar.get(Calendar.MONTH) + 1);
+        assertEquals(22, calendar.get(Calendar.DAY_OF_MONTH));
+
+        assertEquals(15, calendar.get(Calendar.HOUR_OF_DAY));
+        assertEquals(11, calendar.get(Calendar.MINUTE));
+        assertEquals(24, calendar.get(Calendar.SECOND));
+    }
+
+    @Test
+    public void testIso8601Calendar() throws Exception {
+        ContentElement content = TestUtils.parse(contentParser, new ParserOptions().detectCalendarValues(true), file);
+
+        Map<String, Object> props = content.getChild("jcr:content").getProperties();
+
+        Calendar calendar = (Calendar) props.get("dateISO8601String");
+        assertNotNull(calendar);
+
+        assertEquals(2014, calendar.get(Calendar.YEAR));
+        assertEquals(4, calendar.get(Calendar.MONTH) + 1);
+        assertEquals(22, calendar.get(Calendar.DAY_OF_MONTH));
+
+        assertEquals(15, calendar.get(Calendar.HOUR_OF_DAY));
+        assertEquals(11, calendar.get(Calendar.MINUTE));
+        assertEquals(24, calendar.get(Calendar.SECOND));
+    }
+
+    @Test
+    public void testUTF8Chars() throws Exception {
+        ContentElement content = TestUtils.parse(contentParser, file);
+
+        Map<String, Object> props = content.getChild("jcr:content").getProperties();
+
+        assertEquals("äöü߀", props.get("utf8Property"));
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseInvalidJson() throws Exception {
+        file = new File("src/test/resources/invalid-test/invalid.json");
+        ContentElement content = TestUtils.parse(contentParser, file);
+        assertNull(content);
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseInvalidJsonWithObjectList() throws Exception {
+        file = new File("src/test/resources/invalid-test/contentWithObjectList.json");
+        ContentElement content = TestUtils.parse(contentParser, file);
+        assertNull(content);
+    }
+
+    @Test
+    public void testIgnoreResourcesProperties() throws Exception {
+        ContentElement content = TestUtils.parse(
+                contentParser,
+                new ParserOptions().ignoreResourceNames(ImmutableSet.of("header", "newslist", "security:acl", "security:principals"))
+                        .ignorePropertyNames(ImmutableSet.of("jcr:title")), file);
+        ContentElement child = content.getChild("jcr:content");
+
+        assertEquals("Sample Homepage", child.getProperties().get("pageTitle"));
+        assertNull(child.getProperties().get("jcr:title"));
+
+        assertNull(child.getChildren().get("header"));
+        assertNull(child.getChildren().get("newslist"));
+        assertNotNull(child.getChildren().get("lead"));
+
+        assertEquals("abc", child.getProperties().get("refpro1"));
+        assertEquals("def", child.getProperties().get("pathprop1"));
+    }
+
+    @Test
+    public void testGetChild() throws Exception {
+        ContentElement content = TestUtils.parse(contentParser, file);
+        assertNull(content.getName());
+
+        ContentElement deepChild = content.getChild("jcr:content/par/image/file/jcr:content");
+        assertEquals("jcr:content", deepChild.getName());
+        assertEquals("nt:resource", deepChild.getProperties().get("jcr:primaryType"));
+
+        ContentElement invalidChild = content.getChild("non/existing/path");
+        assertNull(invalidChild);
+
+        invalidChild = content.getChild("/jcr:content");
+        assertNull(invalidChild);
+    }
+
+    @Test(expected = ParseException.class)
+    public void testFailsWithoutCommentsEnabled() throws Exception {
+        TestUtils.parse(contentParser, new ParserOptions().jsonParserFeatures(EnumSet.noneOf(JsonParserFeature.class)), file);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/TestUtils.java b/src/test/java/org/apache/sling/contentparser/json/internal/TestUtils.java
new file mode 100644
index 0000000..0d81e36
--- /dev/null
+++ b/src/test/java/org/apache/sling/contentparser/json/internal/TestUtils.java
@@ -0,0 +1,60 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json.internal;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.sling.contentparser.api.ContentParser;
+import org.apache.sling.contentparser.api.ParserOptions;
+import org.apache.sling.contentparser.json.internal.mapsupport.ContentElement;
+import org.apache.sling.contentparser.json.internal.mapsupport.ContentElementHandler;
+
+public final class TestUtils {
+    
+    private TestUtils() {
+        // static methods only
+    }
+
+    static ContentElement parse(ContentParser contentParser, File file) throws IOException {
+        return parse(contentParser, new ParserOptions(), file);
+    }
+
+    static ContentElement parse(ContentParser contentParser, ParserOptions parserOptions, File file) throws IOException {
+        try (FileInputStream fis = new FileInputStream(file);
+             BufferedInputStream bis = new BufferedInputStream(fis)) {
+            ContentElementHandler handler = new ContentElementHandler();
+            contentParser.parse(handler, bis, parserOptions);
+            return handler.getRoot();
+        }
+    }
+    
+    static ContentElement parse(ContentParser contentParser, String jsonContent) throws IOException {
+        try (ByteArrayInputStream is = new ByteArrayInputStream(jsonContent.getBytes(StandardCharsets.UTF_8))) {
+            ContentElementHandler handler = new ContentElementHandler();
+            contentParser.parse(handler, is, new ParserOptions());
+            return handler.getRoot();
+        }
+    }
+    
+}
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElement.java b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElement.java
new file mode 100644
index 0000000..0a4457e
--- /dev/null
+++ b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElement.java
@@ -0,0 +1,52 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json.internal.mapsupport;
+
+import java.util.Map;
+
+/**
+ * Represents a resource or node in the content hierarchy.
+ */
+public interface ContentElement {
+
+    /**
+     * @return Resource name. The root resource has no name (null).
+     */
+    String getName();
+    
+    /**
+     * Properties of this resource.
+     * @return Properties (keys, values)
+     */
+    Map<String, Object> getProperties();
+    
+    /**
+     * Get children of current resource. The Map preserves the ordering of children.
+     * @return Children (child names, child objects)
+     */
+    Map<String, ContentElement> getChildren();
+    
+    /**
+     * Get child or descendant
+     * @param path Relative path to address child or one of it's descendants (use "/" as hierarchy separator).
+     * @return Child or null if no child found with this path
+     */
+    ContentElement getChild(String path);
+    
+}
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementHandler.java b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementHandler.java
new file mode 100644
index 0000000..bc21ec0
--- /dev/null
+++ b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementHandler.java
@@ -0,0 +1,69 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json.internal.mapsupport;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.contentparser.api.ContentHandler;
+
+/**
+ * {@link ContentHandler} implementation that produces a tree of {@link ContentElement} items.
+ */
+public class ContentElementHandler implements ContentHandler {
+    
+    private ContentElement root;
+    private Pattern PATH_PATTERN = Pattern.compile("^((/[^/]+)*)(/([^/]+))$"); 
+
+    @Override
+    public void resource(String path, Map<String, Object> properties) {
+        if (StringUtils.equals(path, "/")) {
+            root = new ContentElementImpl(null, properties);
+        }
+        else {
+            if (root == null) {
+                throw new RuntimeException("Root resource not set.");
+            }
+            Matcher matcher = PATH_PATTERN.matcher(path);
+            if (!matcher.matches()) {
+                throw new RuntimeException("Unexpected path:" + path);
+            }
+            String relativeParentPath = StringUtils.stripStart(matcher.group(1), "/");
+            String name = matcher.group(4);
+            ContentElement parent;
+            if (StringUtils.isEmpty(relativeParentPath)) {
+                parent = root;
+            }
+            else {
+                parent = root.getChild(relativeParentPath);
+            }
+            if (parent == null) {
+                throw new RuntimeException("Parent '" + relativeParentPath + "' does not exist.");
+            }
+            parent.getChildren().put(name, new ContentElementImpl(name, properties));
+        }
+    }
+    
+    public ContentElement getRoot() {
+        return root;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementImpl.java b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementImpl.java
new file mode 100644
index 0000000..ddbf9bd
--- /dev/null
+++ b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementImpl.java
@@ -0,0 +1,68 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json.internal.mapsupport;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+
+final class ContentElementImpl implements ContentElement {
+    
+    private final String name;
+    private final Map<String, Object> properties;
+    private final Map<String, ContentElement> children = new LinkedHashMap<>();
+    
+    public ContentElementImpl(String name, Map<String, Object> properties) {
+        this.name = name;
+        this.properties = properties;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public Map<String, Object> getProperties() {
+        return properties;
+    }
+
+    @Override
+    public Map<String, ContentElement> getChildren() {
+        return children;
+    }
+
+    @Override
+    public ContentElement getChild(String path) {
+        String name = StringUtils.substringBefore(path, "/");
+        ContentElement child = children.get(name);
+        if (child == null) {
+          return null;
+        }
+        String remainingPath = StringUtils.substringAfter(path, "/");
+        if (StringUtils.isEmpty(remainingPath)) {
+          return child;
+        }
+        else {
+          return child.getChild(remainingPath);
+        }
+    }
+
+}
diff --git a/src/test/resources/content-test/content.json b/src/test/resources/content-test/content.json
new file mode 100644
index 0000000..8890044
--- /dev/null
+++ b/src/test/resources/content-test/content.json
@@ -0,0 +1,274 @@
+/* Comment example */
+{
+  "jcr:primaryType": "app:Page",
+  "jcr:createdBy": "admin",
+  "jcr:created": "Thu Aug 07 2014 16:32:59 GMT+0200",
+  /* Comment example */
+  "jcr:content": {
+    "jcr:primaryType": "app:PageContent",  /* Comment example */
+    "jcr:createdBy": "admin",
+    "jcr:title": "English",
+    "app:template": "sample/templates/homepage",
+    "jcr:created": "Thu Aug 07 2014 16:32:59 GMT+0200",
+    "app:lastModified": "Tue Apr 22 2014 15:11:24 GMT+0200",
+    "dateISO8601String": "2014-04-22T15:11:24.000+02:00",
+    "pageTitle": "Sample Homepage",
+    "sling:resourceType": "sample/components/homepage",
+    "sling:resourceSuperType": "sample/components/supertype",
+    "app:designPath": "/etc/designs/sample",
+    "app:lastModifiedBy": "admin",
+    "utf8Property": "äöü߀",
+    "jcr:reference:refpro1": "abc",
+    "jcr:path:pathprop1": "def",
+    /* should be ignored */
+    "security:acl": [
+        { "principal": "TestGroup1", "granted": ["jcr:read","jcr:write"] },
+        { "principal": "TestUser1", "granted": ["jcr:read"], "denied": ["jcr:write"] }
+    ],
+    /* should be ignored */
+    "security:principals": [
+        { "name": "TestUser1", "password": "mypassword", "extraProp1": "extraProp1Value" },
+        { "name": "TestGroup1", "isgroup": "true", "members": ["TestUser1"], "extraProp1": "extraProp1Value" }
+    ],
+    "par": {
+      "jcr:primaryType": "nt:unstructured",
+      "sling:resourceType": "foundation/components/parsys",
+      "colctrl": {
+        "jcr:primaryType": "nt:unstructured",
+        "jcr:createdBy": "admin",
+        "jcr:lastModifiedBy": "admin",
+        "layout": "2;colctrl-lt0",
+        "jcr:created": "Mon Aug 23 2010 22:02:24 GMT+0200",
+        "jcr:lastModified": "Mon Aug 23 2010 22:02:35 GMT+0200",
+        "sling:resourceType": "foundation/components/parsys/colctrl"
+      },
+      "image": {
+        "jcr:primaryType": "nt:unstructured",
+        "jcr:createdBy": "admin",
+        "fileReference": "/content/dam/sample/portraits/jane_doe.jpg",
+        "jcr:lastModifiedBy": "admin",
+        "jcr:created": "Mon Aug 23 2010 22:03:39 GMT+0200",
+        "width": "340",
+        "jcr:lastModified": "Sun Oct 31 2010 21:39:50 GMT+0100",
+        "sling:resourceType": "foundation/components/image",
+        "file": {
+          "jcr:primaryType": "nt:file",
+          "jcr:createdBy": "admin",
+          "jcr:created": "Thu Aug 07 2014 16:32:59 GMT+0200",
+          "jcr:content": {
+            "jcr:primaryType": "nt:resource",
+            "jcr:lastModifiedBy": "anonymous",
+            "jcr:mimeType": "image/jpeg",
+            "jcr:lastModified": "Thu Aug 07 2014 16:32:59 GMT+0200",
+            ":jcr:data": 24377,
+            "jcr:uuid": "eda76d00-b2cd-4b59-878f-c33f71ceaddc"
+          }
+        }
+      },
+      "title_1": {
+        "jcr:primaryType": "nt:unstructured",
+        "jcr:createdBy": "admin",
+        "jcr:title": "Strategic Consulting",
+        "jcr:lastModifiedBy": "admin",
+        "jcr:created": "Mon Aug 23 2010 22:12:08 GMT+0200",
+        "jcr:lastModified": "Wed Oct 27 2010 21:33:24 GMT+0200",
+        "sling:resourceType": "sample/components/title"
+      },
+      "text_1": {
+        "jcr:primaryType": "nt:unstructured",
+        "jcr:createdBy": "admin",
+        "jcr:lastModifiedBy": "admin",
+        "jcr:created": "Sun Oct 31 2010 21:48:04 GMT+0100",
+        "text": "<p><span class=\"Apple-style-span\" style=\"font-size: 12px;\">In&nbsp;today's competitive market, organizations can face several key geometric challenges:<\/span><\/p>\n<ul>\n<li><span class=\"Apple-style-span\" style=\"font-size: 12px;\">Polyhedral Sectioning<\/span><\/li>\n<li><span class=\"Apple-style-span\" style=\"font-size: 12px;\">Triangulation&nbsp;<\/span><\/li>\n<li><span class=\"Apple-style-span\" style=\"font-size: 12px;\">Trigonometric Calculation<\/span><\ [...]
+        "jcr:lastModified": "Sun Oct 31 2010 21:49:06 GMT+0100",
+        "sling:resourceType": "foundation/components/text",
+        "textIsRich": "true"
+      },
+      "col_break12825937554040": {
+        "jcr:primaryType": "nt:unstructured",
+        "controlType": "break",
+        "sling:resourceType": "foundation/components/parsys/colctrl"
+      },
+      "image_0": {
+        "jcr:primaryType": "nt:unstructured",
+        "jcr:createdBy": "admin",
+        "fileReference": "/content/dam/sample/offices/clean_room.jpg",
+        "height": "226",
+        "jcr:lastModifiedBy": "admin",
+        "jcr:created": "Mon Aug 23 2010 22:04:46 GMT+0200",
+        "jcr:lastModified": "Fri Nov 05 2010 10:38:15 GMT+0100",
+        "sling:resourceType": "foundation/components/image",
+        "imageRotate": "0",
+        "file": {
+          "jcr:primaryType": "nt:file",
+          "jcr:createdBy": "admin",
+          "jcr:created": "Thu Aug 07 2014 16:32:59 GMT+0200",
+          "jcr:content": {
+            "jcr:primaryType": "nt:resource",
+            "jcr:lastModifiedBy": "anonymous",
+            "jcr:mimeType": "image/jpeg",
+            "jcr:lastModified": "Thu Aug 07 2014 16:32:59 GMT+0200",
+            ":jcr:data": 21142,
+            "jcr:uuid": "6139077f-191f-4337-aaef-55456ebe6784"
+          }
+        }
+      },
+      "title_2": {
+        "jcr:createdBy": "admin",
+        "jcr:title": "Shape Technology",
+        "jcr:lastModifiedBy": "admin",
+        "jcr:created": "Mon Aug 23 2010 22:12:13 GMT+0200",
+        "jcr:lastModified": "Tue Oct 26 2010 21:16:29 GMT+0200",
+        "sling:resourceType": "sample/components/title"
+      },
+      "text_0": {
+        "jcr:primaryType": "nt:unstructured",
+        "jcr:createdBy": "admin",
+        "jcr:lastModifiedBy": "admin",
+        "jcr:created": "Mon Aug 23 2010 22:16:30 GMT+0200",
+        "text": "<p>The Sample investment in R&amp;D has done more than solidify our industry leadership role, we have now outpaced our competitors to such an extent that we are in an altogether new space.<\/p>\n<p>This is why our high quality polygons and polyhedra provide the only turnkey solutions across the whole range of euclidean geometry. And our mathematicians are working on the next generation of fractal curves to bring you shapes that are unthinkable today.<\/p>\n<p><\/p>\n<p>< [...]
+        "jcr:lastModified": "Mon Nov 08 2010 20:39:00 GMT+0100",
+        "sling:resourceType": "foundation/components/text",
+        "textIsRich": "true"
+      },
+      "col_end12825937444810": {
+        "jcr:primaryType": "nt:unstructured",
+        "controlType": "end",
+        "sling:resourceType": "foundation/components/parsys/colctrl"
+      }
+    },
+    "header": {
+      "jcr:primaryType": "nt:unstructured",
+      "jcr:title": "trust our experience\r\nto manage your business",
+      "imageReference": "/content/dam/sample/header.png",
+      "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nunc eget neque. Nunc condimentum ipsum et orci. Aenean est. Cras eget diam. read more",
+      "sling:resourceType": "sample/components/header"
+    },
+    "newslist": {
+      "jcr:primaryType": "nt:unstructured",
+      "headline": "trust our experience\nto manage your business",
+      "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nunc eget neque. Nunc condimentum ipsum et orci. Aenean est. Cras eget diam. read more",
+      "sling:resourceType": "sample/components/listchildren",
+      "listroot": "/content/sample/en/about/news"
+    },
+    "lead": {
+      "jcr:primaryType": "nt:unstructured",
+      "jcr:title": "World Leader in Applied Geometry ",
+      "jcr:lastModifiedBy": "admin",
+      "text": "Lead Text",
+      "title": "Lead Title",
+      "jcr:description": "Sample has been selling and servicing shapes for over 2000 years. From our beginnings as a small vendor of squares and rectangles we have grown our business into a leading global provider of platonic solids and fractals. Join us as we lead geometry into the future.",
+      "jcr:lastModified": "Wed Jan 19 2011 14:35:29 GMT+0100",
+      "sling:resourceType": "sample/components/lead",
+      "app:annotations": {"jcr:primaryType": "nt:unstructured"}
+    },
+    "image": {
+      "jcr:primaryType": "nt:unstructured",
+      "jcr:lastModifiedBy": "admin",
+      "jcr:lastModified": "Wed Oct 27 2010 21:30:59 GMT+0200",
+      "imageRotate": "0"
+    },
+    "carousel": {
+      "jcr:primaryType": "nt:unstructured",
+      "playSpeed": "6000",
+      "jcr:lastModifiedBy": "admin",
+      "pages": [
+        "/content/sample/en/events/techsummit",
+        "/content/sample/en/events/userconf",
+        "/content/sample/en/events/shapecon",
+        "/content/sample/en/events/dsc"
+      ],
+      "jcr:lastModified": "Tue Oct 05 2010 14:14:27 GMT+0200",
+      "transTime": "1000",
+      "sling:resourceType": "foundation/components/carousel",
+      "listFrom": "static"
+    },
+    "rightpar": {
+      "jcr:primaryType": "nt:unstructured",
+      "sling:resourceType": "foundation/components/parsys",
+      "teaser": {
+        "jcr:primaryType": "nt:unstructured",
+        "jcr:createdBy": "admin",
+        "jcr:lastModifiedBy": "admin",
+        "jcr:created": "Tue Jan 25 2011 11:30:09 GMT+0100",
+        "campaignpath": "/content/campaigns/sample",
+        "jcr:lastModified": "Wed Feb 02 2011 08:40:30 GMT+0100",
+        "sling:resourceType": "personalization/components/teaser"
+      }
+    }
+  },
+  "toolbar": {
+    "jcr:primaryType": "app:Page",
+    "jcr:createdBy": "admin",
+    "jcr:created": "Thu Aug 07 2014 16:33:00 GMT+0200",
+    "jcr:content": {
+      "jcr:primaryType": "app:PageContent",
+      "subtitle": "Contains the toolbar",
+      "jcr:createdBy": "admin",
+      "jcr:title": "Toolbar",
+      "app:template": "sample/templates/contentpage",
+      "jcr:created": "Thu Aug 07 2014 16:33:00 GMT+0200",
+      "app:lastModified": "Wed Aug 25 2010 22:51:02 GMT+0200",
+      "hideInNav": "true",
+      "sling:resourceType": "sample/components/contentpage",
+      "app:lastModifiedBy": "admin",
+      "par": {
+        "jcr:primaryType": "nt:unstructured",
+        "sling:resourceType": "foundation/components/parsys"
+      },
+      "rightpar": {
+        "jcr:primaryType": "nt:unstructured",
+        "sling:resourceType": "foundation/components/iparsys",
+        "iparsys_fake_par": {
+          "jcr:primaryType": "nt:unstructured",
+          "sling:resourceType": "foundation/components/iparsys/par"
+        }
+      }
+    },
+    "profiles": {
+      "jcr:primaryType": "app:Page",
+      "jcr:createdBy": "admin",
+      "jcr:created": "Thu Aug 07 2014 16:33:00 GMT+0200",
+      "jcr:content": {
+        "jcr:primaryType": "app:PageContent",
+        "jcr:mixinTypes": ["type1","type2"],
+        "jcr:createdBy": "admin",
+        "jcr:title": "Profiles",
+        "app:template": "sample/templates/contentpage",
+        "jcr:created": "Thu Aug 07 2014 16:33:00 GMT+0200",
+        "app:lastModified": "Thu Nov 05 2009 20:27:13 GMT+0100",
+        "hideInNav": true,
+        "sling:resourceType": "sample/components/contentpage",
+        "app:lastModifiedBy": "admin",
+        "longProp": 1234567890123,
+        "decimalProp": 1.2345,
+        "booleanProp": true,
+        "longPropMulti": [1234567890123,55],
+        "decimalPropMulti": [1.2345,1.1],
+        "booleanPropMulti": [true,false],
+        "stringPropMulti": ["aa","bb","cc"],
+        "par": {
+          "jcr:primaryType": "nt:unstructured",
+          "sling:resourceType": "foundation/components/parsys",
+          "textimage": {
+            "jcr:primaryType": "nt:unstructured",
+            "sling:resourceType": "foundation/components/textimage"
+          },
+          "mygadgets": {
+            "jcr:primaryType": "nt:unstructured",
+            "gadgets": "http://customer.meteogroup.de/meteogroup/gadgets/wetter24.xml\nhttp://germanweatherradar.googlecode.com/svn/trunk/german-weather-radar.xml\nhttp://www.digitalpowered.info/gadget/ski.pictures.xml\nhttp://www.canbuffi.de/gadgets/clock/clock.xml",
+            "sling:resourceType": "personalization/components/mygadgets"
+          }
+        },
+        "rightpar": {
+          "jcr:primaryType": "nt:unstructured",
+          "sling:resourceType": "foundation/components/iparsys",
+          "iparsys_fake_par": {
+            "jcr:primaryType": "nt:unstructured",
+            "sling:resourceType": "foundation/components/iparsys/par"
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/resources/invalid-test/contentWithObjectList.json b/src/test/resources/invalid-test/contentWithObjectList.json
new file mode 100644
index 0000000..e4527a0
--- /dev/null
+++ b/src/test/resources/invalid-test/contentWithObjectList.json
@@ -0,0 +1,14 @@
+{
+  "prop1": "value1",
+  "childObject": {
+    "prop2": "value2"
+  },
+  "childObjectList": [
+    {
+      "prop1": "value1"
+    },
+    {
+      "prop2": "value2"
+    }
+  ]
+}
diff --git a/src/test/resources/invalid-test/invalid.json b/src/test/resources/invalid-test/invalid.json
new file mode 100644
index 0000000..59fc86c
--- /dev/null
+++ b/src/test/resources/invalid-test/invalid.json
@@ -0,0 +1 @@
+This is invalid json.


[sling-org-apache-sling-contentparser-json] 05/08: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git

commit 37a15b401a0bbb2a4e0cf90a0bdb6c03dc096732
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Mon Jul 15 13:53:24 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * configured bnd-baseline-maven-plugin to not fail the build when
    there's no artifact available for the baseline analysis
---
 pom.xml | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/pom.xml b/pom.xml
index 99d3bd0..9099d50 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,18 @@
         <tag>HEAD</tag>
     </scm>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-baseline-maven-plugin</artifactId>
+                <configuration>
+                    <failOnMissing>false</failOnMissing>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
     <dependencies>
         <dependency>
             <groupId>org.apache.sling</groupId>


[sling-org-apache-sling-contentparser-json] 04/08: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git

commit e6239df5616474e37d6da744a1f0e5d03ef48ef1
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Mon Jul 15 12:02:03 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * added parser for JCR-flavoured XML
    * made the ParserHelper compatible with both Java 8 and Java 11
    * reduced the number of dependencies for all modules
---
 pom.xml                                            | 27 ---------------
 .../json/internal/JsonTicksConverter.java          |  3 +-
 .../json/internal/JsonContentParserTest.java       | 39 +++++++++++++---------
 3 files changed, 24 insertions(+), 45 deletions(-)

diff --git a/pom.xml b/pom.xml
index 7e6dbd9..99d3bd0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,32 +62,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-lang3</artifactId>
-            <version>3.8</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>org.osgi.service.component.annotations</artifactId>
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.service.component</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.framework</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.contentparser.testutils</artifactId>
             <version>0.9.0-SNAPSHOT</version>
@@ -99,12 +78,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-            <version>15.0</version>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.apache.johnzon</groupId>
             <artifactId>johnzon-core</artifactId>
             <version>1.0.0</version>
diff --git a/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java b/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java
index df0be76..15b6553 100644
--- a/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java
+++ b/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java
@@ -59,8 +59,7 @@ final class JsonTicksConverter {
                     if (in == '"') {
                         if (quoted) {
                             quoted = false;
-                        }
-                        else if (tickQuoted) {
+                        } else {
                             output.append("\\");
                         }
                     }
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java b/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
index b52e934..f82c8a4 100644
--- a/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
+++ b/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
@@ -20,8 +20,11 @@ package org.apache.sling.contentparser.json.internal;
 
 import java.io.File;
 import java.math.BigDecimal;
+import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.TimeZone;
 
@@ -34,8 +37,6 @@ import org.apache.sling.contentparser.testutils.mapsupport.ContentElement;
 import org.junit.Before;
 import org.junit.Test;
 
-import com.google.common.collect.ImmutableSet;
-
 import static junit.framework.TestCase.assertNull;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -62,8 +63,9 @@ public class JsonContentParserTest {
     @Test
     public void testDataTypes() throws Exception {
         ContentElement content = TestUtils.parse(contentParser, file);
-
-        Map<String, Object> props = content.getChild("toolbar/profiles/jcr:content").getProperties();
+        ContentElement child = content.getChild("toolbar/profiles/jcr:content");
+        assertNotNull("Expected child at toolbar/profiles/jcr:content", child);
+        Map<String, Object> props = child.getProperties();
         assertEquals(true, props.get("hideInNav"));
 
         assertEquals(1234567890123L, props.get("longProp"));
@@ -78,16 +80,18 @@ public class JsonContentParserTest {
     @Test
     public void testContentProperties() throws Exception {
         ContentElement content = TestUtils.parse(contentParser, file);
-
-        Map<String, Object> props = content.getChild("jcr:content/header").getProperties();
+        ContentElement child = content.getChild("jcr:content/header");
+        assertNotNull("Expected child at jcr:content/header", child);
+        Map<String, Object> props = child.getProperties();
         assertEquals("/content/dam/sample/header.png", props.get("imageReference"));
     }
 
     @Test
     public void testCalendar() throws Exception {
         ContentElement content = TestUtils.parse(contentParser, new ParserOptions().detectCalendarValues(true), file);
-
-        Map<String, Object> props = content.getChild("jcr:content").getProperties();
+        ContentElement child = content.getChild("jcr:content");
+        assertNotNull("Expected child at jcr:content", child);
+        Map<String, Object> props = child.getProperties();
 
         Calendar calendar = (Calendar) props.get("app:lastModified");
         assertNotNull(calendar);
@@ -106,8 +110,9 @@ public class JsonContentParserTest {
     @Test
     public void testIso8601Calendar() throws Exception {
         ContentElement content = TestUtils.parse(contentParser, new ParserOptions().detectCalendarValues(true), file);
-
-        Map<String, Object> props = content.getChild("jcr:content").getProperties();
+        ContentElement child = content.getChild("jcr:content");
+        assertNotNull("Expected child at jcr:content", child);
+        Map<String, Object> props = child.getProperties();
 
         Calendar calendar = (Calendar) props.get("dateISO8601String");
         assertNotNull(calendar);
@@ -124,8 +129,9 @@ public class JsonContentParserTest {
     @Test
     public void testUTF8Chars() throws Exception {
         ContentElement content = TestUtils.parse(contentParser, file);
-
-        Map<String, Object> props = content.getChild("jcr:content").getProperties();
+        ContentElement child = content.getChild("jcr:content");
+        assertNotNull("Expected child at jcr:content", child);
+        Map<String, Object> props = child.getProperties();
 
         assertEquals("äöü߀", props.get("utf8Property"));
     }
@@ -148,10 +154,11 @@ public class JsonContentParserTest {
     public void testIgnoreResourcesProperties() throws Exception {
         ContentElement content = TestUtils.parse(
                 contentParser,
-                new ParserOptions().ignoreResourceNames(ImmutableSet.of("header", "newslist", "security:acl", "security:principals"))
-                        .ignorePropertyNames(ImmutableSet.of("jcr:title")), file);
+                new ParserOptions().ignoreResourceNames(Collections
+                        .unmodifiableSet(new HashSet<>(Arrays.asList("header", "newslist", "security:acl", "security:principals"))))
+                        .ignorePropertyNames(Collections.unmodifiableSet(new HashSet<>(Arrays.asList("jcr:title")))), file);
         ContentElement child = content.getChild("jcr:content");
-
+        assertNotNull("Expected child at jcr:content", child);
         assertEquals("Sample Homepage", child.getProperties().get("pageTitle"));
         assertNull(child.getProperties().get("jcr:title"));
 
@@ -167,8 +174,8 @@ public class JsonContentParserTest {
     public void testGetChild() throws Exception {
         ContentElement content = TestUtils.parse(contentParser, file);
         assertNull(content.getName());
-
         ContentElement deepChild = content.getChild("jcr:content/par/image/file/jcr:content");
+        assertNotNull("Expected child at jcr:content/par/image/file/jcr:content", deepChild);
         assertEquals("jcr:content", deepChild.getName());
         assertEquals("nt:resource", deepChild.getProperties().get("jcr:primaryType"));
 


[sling-org-apache-sling-contentparser-json] 03/08: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git

commit e6ac640b671ef2d660191b25dffff3f0bf442d05
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Fri Jul 12 16:33:23 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * extracted a test utils module, which can be used by all contentparser
    implementations
    * added a XML Content Parser implementation
---
 pom.xml                                            |  6 ++
 .../json/internal/JsonContentParserTest.java       |  3 +-
 .../contentparser/json/internal/TestUtils.java     | 60 -------------------
 .../json/internal/mapsupport/ContentElement.java   | 52 ----------------
 .../internal/mapsupport/ContentElementHandler.java | 69 ----------------------
 .../internal/mapsupport/ContentElementImpl.java    | 68 ---------------------
 6 files changed, 8 insertions(+), 250 deletions(-)

diff --git a/pom.xml b/pom.xml
index 3fc188b..7e6dbd9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,6 +88,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.contentparser.testutils</artifactId>
+            <version>0.9.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java b/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
index 3099ae8..b52e934 100644
--- a/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
+++ b/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
@@ -29,7 +29,8 @@ import org.apache.sling.contentparser.api.ContentParser;
 import org.apache.sling.contentparser.api.JsonParserFeature;
 import org.apache.sling.contentparser.api.ParseException;
 import org.apache.sling.contentparser.api.ParserOptions;
-import org.apache.sling.contentparser.json.internal.mapsupport.ContentElement;
+import org.apache.sling.contentparser.testutils.TestUtils;
+import org.apache.sling.contentparser.testutils.mapsupport.ContentElement;
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/TestUtils.java b/src/test/java/org/apache/sling/contentparser/json/internal/TestUtils.java
deleted file mode 100644
index 0d81e36..0000000
--- a/src/test/java/org/apache/sling/contentparser/json/internal/TestUtils.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ~ Licensed to the Apache Software Foundation (ASF) under one
- ~ or more contributor license agreements.  See the NOTICE file
- ~ distributed with this work for additional information
- ~ regarding copyright ownership.  The ASF licenses this file
- ~ to you under the Apache License, Version 2.0 (the
- ~ "License"); you may not use this file except in compliance
- ~ with the License.  You may obtain a copy of the License at
- ~
- ~   http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing,
- ~ software distributed under the License is distributed on an
- ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- ~ KIND, either express or implied.  See the License for the
- ~ specific language governing permissions and limitations
- ~ under the License.
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-package org.apache.sling.contentparser.json.internal;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import org.apache.sling.contentparser.api.ContentParser;
-import org.apache.sling.contentparser.api.ParserOptions;
-import org.apache.sling.contentparser.json.internal.mapsupport.ContentElement;
-import org.apache.sling.contentparser.json.internal.mapsupport.ContentElementHandler;
-
-public final class TestUtils {
-    
-    private TestUtils() {
-        // static methods only
-    }
-
-    static ContentElement parse(ContentParser contentParser, File file) throws IOException {
-        return parse(contentParser, new ParserOptions(), file);
-    }
-
-    static ContentElement parse(ContentParser contentParser, ParserOptions parserOptions, File file) throws IOException {
-        try (FileInputStream fis = new FileInputStream(file);
-             BufferedInputStream bis = new BufferedInputStream(fis)) {
-            ContentElementHandler handler = new ContentElementHandler();
-            contentParser.parse(handler, bis, parserOptions);
-            return handler.getRoot();
-        }
-    }
-    
-    static ContentElement parse(ContentParser contentParser, String jsonContent) throws IOException {
-        try (ByteArrayInputStream is = new ByteArrayInputStream(jsonContent.getBytes(StandardCharsets.UTF_8))) {
-            ContentElementHandler handler = new ContentElementHandler();
-            contentParser.parse(handler, is, new ParserOptions());
-            return handler.getRoot();
-        }
-    }
-    
-}
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElement.java b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElement.java
deleted file mode 100644
index 0a4457e..0000000
--- a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElement.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ~ Licensed to the Apache Software Foundation (ASF) under one
- ~ or more contributor license agreements.  See the NOTICE file
- ~ distributed with this work for additional information
- ~ regarding copyright ownership.  The ASF licenses this file
- ~ to you under the Apache License, Version 2.0 (the
- ~ "License"); you may not use this file except in compliance
- ~ with the License.  You may obtain a copy of the License at
- ~
- ~   http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing,
- ~ software distributed under the License is distributed on an
- ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- ~ KIND, either express or implied.  See the License for the
- ~ specific language governing permissions and limitations
- ~ under the License.
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-package org.apache.sling.contentparser.json.internal.mapsupport;
-
-import java.util.Map;
-
-/**
- * Represents a resource or node in the content hierarchy.
- */
-public interface ContentElement {
-
-    /**
-     * @return Resource name. The root resource has no name (null).
-     */
-    String getName();
-    
-    /**
-     * Properties of this resource.
-     * @return Properties (keys, values)
-     */
-    Map<String, Object> getProperties();
-    
-    /**
-     * Get children of current resource. The Map preserves the ordering of children.
-     * @return Children (child names, child objects)
-     */
-    Map<String, ContentElement> getChildren();
-    
-    /**
-     * Get child or descendant
-     * @param path Relative path to address child or one of it's descendants (use "/" as hierarchy separator).
-     * @return Child or null if no child found with this path
-     */
-    ContentElement getChild(String path);
-    
-}
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementHandler.java b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementHandler.java
deleted file mode 100644
index bc21ec0..0000000
--- a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementHandler.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ~ Licensed to the Apache Software Foundation (ASF) under one
- ~ or more contributor license agreements.  See the NOTICE file
- ~ distributed with this work for additional information
- ~ regarding copyright ownership.  The ASF licenses this file
- ~ to you under the Apache License, Version 2.0 (the
- ~ "License"); you may not use this file except in compliance
- ~ with the License.  You may obtain a copy of the License at
- ~
- ~   http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing,
- ~ software distributed under the License is distributed on an
- ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- ~ KIND, either express or implied.  See the License for the
- ~ specific language governing permissions and limitations
- ~ under the License.
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-package org.apache.sling.contentparser.json.internal.mapsupport;
-
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.sling.contentparser.api.ContentHandler;
-
-/**
- * {@link ContentHandler} implementation that produces a tree of {@link ContentElement} items.
- */
-public class ContentElementHandler implements ContentHandler {
-    
-    private ContentElement root;
-    private Pattern PATH_PATTERN = Pattern.compile("^((/[^/]+)*)(/([^/]+))$"); 
-
-    @Override
-    public void resource(String path, Map<String, Object> properties) {
-        if (StringUtils.equals(path, "/")) {
-            root = new ContentElementImpl(null, properties);
-        }
-        else {
-            if (root == null) {
-                throw new RuntimeException("Root resource not set.");
-            }
-            Matcher matcher = PATH_PATTERN.matcher(path);
-            if (!matcher.matches()) {
-                throw new RuntimeException("Unexpected path:" + path);
-            }
-            String relativeParentPath = StringUtils.stripStart(matcher.group(1), "/");
-            String name = matcher.group(4);
-            ContentElement parent;
-            if (StringUtils.isEmpty(relativeParentPath)) {
-                parent = root;
-            }
-            else {
-                parent = root.getChild(relativeParentPath);
-            }
-            if (parent == null) {
-                throw new RuntimeException("Parent '" + relativeParentPath + "' does not exist.");
-            }
-            parent.getChildren().put(name, new ContentElementImpl(name, properties));
-        }
-    }
-    
-    public ContentElement getRoot() {
-        return root;
-    }
-
-}
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementImpl.java b/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementImpl.java
deleted file mode 100644
index ddbf9bd..0000000
--- a/src/test/java/org/apache/sling/contentparser/json/internal/mapsupport/ContentElementImpl.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ~ Licensed to the Apache Software Foundation (ASF) under one
- ~ or more contributor license agreements.  See the NOTICE file
- ~ distributed with this work for additional information
- ~ regarding copyright ownership.  The ASF licenses this file
- ~ to you under the Apache License, Version 2.0 (the
- ~ "License"); you may not use this file except in compliance
- ~ with the License.  You may obtain a copy of the License at
- ~
- ~   http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing,
- ~ software distributed under the License is distributed on an
- ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- ~ KIND, either express or implied.  See the License for the
- ~ specific language governing permissions and limitations
- ~ under the License.
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-package org.apache.sling.contentparser.json.internal.mapsupport;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.apache.commons.lang3.StringUtils;
-
-final class ContentElementImpl implements ContentElement {
-    
-    private final String name;
-    private final Map<String, Object> properties;
-    private final Map<String, ContentElement> children = new LinkedHashMap<>();
-    
-    public ContentElementImpl(String name, Map<String, Object> properties) {
-        this.name = name;
-        this.properties = properties;
-    }
-
-    @Override
-    public String getName() {
-        return name;
-    }
-
-    @Override
-    public Map<String, Object> getProperties() {
-        return properties;
-    }
-
-    @Override
-    public Map<String, ContentElement> getChildren() {
-        return children;
-    }
-
-    @Override
-    public ContentElement getChild(String path) {
-        String name = StringUtils.substringBefore(path, "/");
-        ContentElement child = children.get(name);
-        if (child == null) {
-          return null;
-        }
-        String remainingPath = StringUtils.substringAfter(path, "/");
-        if (StringUtils.isEmpty(remainingPath)) {
-          return child;
-        }
-        else {
-          return child.getChild(remainingPath);
-        }
-    }
-
-}


[sling-org-apache-sling-contentparser-json] 02/08: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git

commit f77d16d0ee91cfc23731e60eb96c4013650db18b
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Thu Jul 11 15:23:46 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * pom clean-up
    * replaced a deprecated import and removed an unused one
---
 pom.xml                                                | 18 +++++++-----------
 .../contentparser/json/internal/JsonContentParser.java |  5 ++---
 2 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/pom.xml b/pom.xml
index a33598b..3fc188b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,35 +17,31 @@
     specific language governing permissions and limitations
     under the License.
 -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling-bundle-parent</artifactId>
         <version>35</version>
-        <relativePath />
+        <relativePath/>
     </parent>
 
     <artifactId>org.apache.sling.contentparser.json</artifactId>
     <version>0.9.0-SNAPSHOT</version>
 
-    <name>Apache Sling Content Parser API</name>
+    <name>Apache Sling Content Parser for JSON</name>
     <description>
-        Parser API Apache Sling Resource trees stored in files (e.g. JSON, FileVault XML, etc.).
+        Apache Sling Content Parser for resource trees stored in JSON files
     </description>
 
     <scm>
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-contentparser-json.git</url>
-      <tag>HEAD</tag>
-  </scm>
+        <tag>HEAD</tag>
+    </scm>
 
-    <build>
-        <plugins>
-            
-        </plugins>
-    </build>
     <dependencies>
         <dependency>
             <groupId>org.apache.sling</groupId>
diff --git a/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java b/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java
index 16d47e0..33f048e 100644
--- a/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java
+++ b/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java
@@ -21,11 +21,11 @@ package org.apache.sling.contentparser.json.internal;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -40,7 +40,6 @@ import javax.json.JsonValue;
 import javax.json.stream.JsonParsingException;
 
 import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.CharEncoding;
 import org.apache.sling.contentparser.api.ContentHandler;
 import org.apache.sling.contentparser.api.ContentParser;
 import org.apache.sling.contentparser.api.JsonParserFeature;
@@ -88,7 +87,7 @@ public class JsonContentParser implements ContentParser {
     private JsonObject toJsonObjectWithJsonTicks(JsonReaderFactory jsonReaderFactory, InputStream is) {
         String jsonString;
         try {
-            jsonString = IOUtils.toString(is, CharEncoding.UTF_8);
+            jsonString = IOUtils.toString(is, StandardCharsets.UTF_8);
         } catch (IOException ex) {
             throw new ParseException("Error getting JSON string.", ex);
         }


[sling-org-apache-sling-contentparser-json] 07/08: SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-contentparser-json.git

commit 65cd42cc43b162cf47c6d58516cc569db4f63379
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Mon Jul 22 09:39:40 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * completely decoupled the API from implementations - the ContentParser API
    doesn't suggest any content types any more
    * removed JSON specific parser options from the ParserOptions class
    * made the ParserOptions class extendable
    * switched exported API packages to version 2.0.0, to eliminate all possible
    confusion with the older org.apache.sling.jcr.contentparser API
---
 README.md                                          |  2 +-
 pom.xml                                            |  5 ++
 .../contentparser/json/JSONParserFeature.java      | 36 +++++++++++
 .../contentparser/json/JSONParserOptions.java      | 73 ++++++++++++++++++++++
 ...onContentParser.java => JSONContentParser.java} | 22 +++++--
 ...TicksConverter.java => JSONTicksConverter.java} |  4 +-
 .../sling/contentparser/json/package-info.java     | 22 +++++++
 ...tParserTest.java => JSONContentParserTest.java} | 26 ++++----
 8 files changed, 168 insertions(+), 22 deletions(-)

diff --git a/README.md b/README.md
index 0359394..1b07e37 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,6 @@ To obtain a reference to the JSON content parser just filter on the `ContentPars
 property:
 
 ```java
-    @Reference(target = "(" + ContentParser.SERVICE_PROPERTY_CONTENT_TYPE + "=" + ContentParser.JSON_CONTENT_TYPE + ")")
+    @Reference(target = "(" + ContentParser.SERVICE_PROPERTY_CONTENT_TYPE + "=json)")
     private ContentParser jsonParser;
 ``` 
diff --git a/pom.xml b/pom.xml
index 9099d50..9ae7111 100644
--- a/pom.xml
+++ b/pom.xml
@@ -79,6 +79,11 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.annotation.versioning</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.contentparser.testutils</artifactId>
             <version>0.9.0-SNAPSHOT</version>
diff --git a/src/main/java/org/apache/sling/contentparser/json/JSONParserFeature.java b/src/main/java/org/apache/sling/contentparser/json/JSONParserFeature.java
new file mode 100644
index 0000000..3d2b5f8
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/json/JSONParserFeature.java
@@ -0,0 +1,36 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json;
+
+/**
+ * Feature flags for parsing JSON files.
+ */
+public enum JSONParserFeature {
+
+    /**
+     * Support comments (&#47;* ... *&#47;) in JSON files.
+     */
+    COMMENTS,
+
+    /**
+     * Support ticks (') additional to double quotes (") as quoting symbol for JSON names and strings.
+     */
+    QUOTE_TICK
+    
+}
diff --git a/src/main/java/org/apache/sling/contentparser/json/JSONParserOptions.java b/src/main/java/org/apache/sling/contentparser/json/JSONParserOptions.java
new file mode 100644
index 0000000..0fe1ac0
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/json/JSONParserOptions.java
@@ -0,0 +1,73 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.contentparser.json;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+
+import org.apache.sling.contentparser.api.ParserOptions;
+import org.osgi.annotation.versioning.ConsumerType;
+
+/**
+ * Defines specific JSON parser options which can be used with the JSON {@link org.apache.sling.contentparser.api.ContentParser}
+ * implementations provided by this bundle.
+ */
+@ConsumerType
+public final class JSONParserOptions extends ParserOptions {
+
+    /**
+     * List of JSON parser features activated by default.
+     */
+    public static final EnumSet<JSONParserFeature> DEFAULT_JSON_PARSER_FEATURES
+            = EnumSet.of(JSONParserFeature.COMMENTS);
+
+    private EnumSet<JSONParserFeature> features = DEFAULT_JSON_PARSER_FEATURES;
+
+    /**
+     * Set the features the JSON parser should apply when parsing files.
+     *
+     * @param value JSON parser features
+     * @return this
+     */
+    public JSONParserOptions withFeatures(EnumSet<JSONParserFeature> value) {
+        this.features = EnumSet.copyOf(value);
+        return this;
+    }
+
+    /**
+     * Set the features the JSON parser should apply when parsing files.
+     *
+     * @param value JSON parser features
+     * @return this
+     */
+    public JSONParserOptions withFeatures(JSONParserFeature... value) {
+        this.features = EnumSet.copyOf(Arrays.asList(value));
+        return this;
+    }
+
+    /**
+     * Returns a copy of the features encapsulated by this instance. For modifying the set of features please use the {@code withFeatures}
+     * fluid methods.
+     *
+     * @return the features the JSON parser should apply when parsing files
+     */
+    public EnumSet<JSONParserFeature> getFeatures() {
+        return EnumSet.copyOf(features);
+    }
+}
diff --git a/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java b/src/main/java/org/apache/sling/contentparser/json/internal/JSONContentParser.java
similarity index 91%
rename from src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java
rename to src/main/java/org/apache/sling/contentparser/json/internal/JSONContentParser.java
index 33f048e..831ee22 100644
--- a/src/main/java/org/apache/sling/contentparser/json/internal/JsonContentParser.java
+++ b/src/main/java/org/apache/sling/contentparser/json/internal/JSONContentParser.java
@@ -42,22 +42,32 @@ import javax.json.stream.JsonParsingException;
 import org.apache.commons.io.IOUtils;
 import org.apache.sling.contentparser.api.ContentHandler;
 import org.apache.sling.contentparser.api.ContentParser;
-import org.apache.sling.contentparser.api.JsonParserFeature;
 import org.apache.sling.contentparser.api.ParseException;
 import org.apache.sling.contentparser.api.ParserHelper;
 import org.apache.sling.contentparser.api.ParserOptions;
+import org.apache.sling.contentparser.json.JSONParserFeature;
+import org.apache.sling.contentparser.json.JSONParserOptions;
 import org.osgi.service.component.annotations.Component;
 
 @Component(
         property = {
-                ContentParser.SERVICE_PROPERTY_CONTENT_TYPE + "=" + ContentParser.JSON_CONTENT_TYPE
+                ContentParser.SERVICE_PROPERTY_CONTENT_TYPE + "=json"
         }
 )
-public class JsonContentParser implements ContentParser {
+public class JSONContentParser implements ContentParser {
 
     @Override
     public void parse(ContentHandler handler, InputStream is, ParserOptions parserOptions) throws ParseException {
-        final boolean jsonQuoteTicks = parserOptions.getJsonParserFeatures().contains(JsonParserFeature.QUOTE_TICK);
+        final boolean jsonQuoteTicks;
+        final boolean supportComments;
+        if (parserOptions instanceof JSONParserOptions) {
+            JSONParserOptions jsonParserOptions = (JSONParserOptions) parserOptions;
+            jsonQuoteTicks = jsonParserOptions.getFeatures().contains(JSONParserFeature.QUOTE_TICK);
+            supportComments = jsonParserOptions.getFeatures().contains(JSONParserFeature.COMMENTS);
+        } else {
+            jsonQuoteTicks = false;
+            supportComments = false;
+        }
 
         /*
          * Implementation note: This parser uses JsonReader instead of the (more memory-efficient)
@@ -66,7 +76,7 @@ public class JsonContentParser implements ContentParser {
          */
         final JsonReaderFactory jsonReaderFactory =
                 Json.createReaderFactory(
-                        parserOptions.getJsonParserFeatures().contains(JsonParserFeature.COMMENTS) ?
+                        supportComments ?
                                 new HashMap<String, Object>() {{
                                     put("org.apache.johnzon.supports-comments", true);
                                 }} :
@@ -93,7 +103,7 @@ public class JsonContentParser implements ContentParser {
         }
 
         // convert ticks to double quotes
-        jsonString = JsonTicksConverter.tickToDoubleQuote(jsonString);
+        jsonString = JSONTicksConverter.tickToDoubleQuote(jsonString);
 
         try (JsonReader reader = jsonReaderFactory.createReader(new StringReader(jsonString))) {
             return reader.readObject();
diff --git a/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java b/src/main/java/org/apache/sling/contentparser/json/internal/JSONTicksConverter.java
similarity index 98%
rename from src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java
rename to src/main/java/org/apache/sling/contentparser/json/internal/JSONTicksConverter.java
index 15b6553..83d0c07 100644
--- a/src/main/java/org/apache/sling/contentparser/json/internal/JsonTicksConverter.java
+++ b/src/main/java/org/apache/sling/contentparser/json/internal/JSONTicksConverter.java
@@ -29,9 +29,9 @@ package org.apache.sling.contentparser.json.internal;
  *     After the conversion they are always escaped.</li>
  * </ul>
  */
-final class JsonTicksConverter {
+final class JSONTicksConverter {
     
-    private JsonTicksConverter() {
+    private JSONTicksConverter() {
         // static methods only
     }
     
diff --git a/src/main/java/org/apache/sling/contentparser/json/package-info.java b/src/main/java/org/apache/sling/contentparser/json/package-info.java
new file mode 100644
index 0000000..3e86af2
--- /dev/null
+++ b/src/main/java/org/apache/sling/contentparser/json/package-info.java
@@ -0,0 +1,22 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License.  You may obtain a copy of the License at
+ ~
+ ~   http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied.  See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+@Version("2.0.0")
+package org.apache.sling.contentparser.json;
+
+import org.osgi.annotation.versioning.Version;
diff --git a/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java b/src/test/java/org/apache/sling/contentparser/json/internal/JSONContentParserTest.java
similarity index 92%
rename from src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
rename to src/test/java/org/apache/sling/contentparser/json/internal/JSONContentParserTest.java
index f82c8a4..658151d 100644
--- a/src/test/java/org/apache/sling/contentparser/json/internal/JsonContentParserTest.java
+++ b/src/test/java/org/apache/sling/contentparser/json/internal/JSONContentParserTest.java
@@ -29,9 +29,9 @@ import java.util.Map;
 import java.util.TimeZone;
 
 import org.apache.sling.contentparser.api.ContentParser;
-import org.apache.sling.contentparser.api.JsonParserFeature;
 import org.apache.sling.contentparser.api.ParseException;
-import org.apache.sling.contentparser.api.ParserOptions;
+import org.apache.sling.contentparser.json.JSONParserFeature;
+import org.apache.sling.contentparser.json.JSONParserOptions;
 import org.apache.sling.contentparser.testutils.TestUtils;
 import org.apache.sling.contentparser.testutils.mapsupport.ContentElement;
 import org.junit.Before;
@@ -42,7 +42,7 @@ import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-public class JsonContentParserTest {
+public class JSONContentParserTest {
 
     private File file;
     private ContentParser contentParser;
@@ -50,19 +50,19 @@ public class JsonContentParserTest {
     @Before
     public void setUp() {
         file = new File("src/test/resources/content-test/content.json");
-        contentParser = new JsonContentParser();
+        contentParser = new JSONContentParser();
     }
 
     @Test
     public void testPageJcrPrimaryType() throws Exception {
-        ContentElement content = TestUtils.parse(contentParser, file);
+        ContentElement content = TestUtils.parse(contentParser, new JSONParserOptions(), file);
 
         assertEquals("app:Page", content.getProperties().get("jcr:primaryType"));
     }
 
     @Test
     public void testDataTypes() throws Exception {
-        ContentElement content = TestUtils.parse(contentParser, file);
+        ContentElement content = TestUtils.parse(contentParser, new JSONParserOptions(), file);
         ContentElement child = content.getChild("toolbar/profiles/jcr:content");
         assertNotNull("Expected child at toolbar/profiles/jcr:content", child);
         Map<String, Object> props = child.getProperties();
@@ -79,7 +79,7 @@ public class JsonContentParserTest {
 
     @Test
     public void testContentProperties() throws Exception {
-        ContentElement content = TestUtils.parse(contentParser, file);
+        ContentElement content = TestUtils.parse(contentParser, new JSONParserOptions(), file);
         ContentElement child = content.getChild("jcr:content/header");
         assertNotNull("Expected child at jcr:content/header", child);
         Map<String, Object> props = child.getProperties();
@@ -88,7 +88,7 @@ public class JsonContentParserTest {
 
     @Test
     public void testCalendar() throws Exception {
-        ContentElement content = TestUtils.parse(contentParser, new ParserOptions().detectCalendarValues(true), file);
+        ContentElement content = TestUtils.parse(contentParser, new JSONParserOptions().detectCalendarValues(true), file);
         ContentElement child = content.getChild("jcr:content");
         assertNotNull("Expected child at jcr:content", child);
         Map<String, Object> props = child.getProperties();
@@ -109,7 +109,7 @@ public class JsonContentParserTest {
 
     @Test
     public void testIso8601Calendar() throws Exception {
-        ContentElement content = TestUtils.parse(contentParser, new ParserOptions().detectCalendarValues(true), file);
+        ContentElement content = TestUtils.parse(contentParser, new JSONParserOptions().detectCalendarValues(true), file);
         ContentElement child = content.getChild("jcr:content");
         assertNotNull("Expected child at jcr:content", child);
         Map<String, Object> props = child.getProperties();
@@ -128,7 +128,7 @@ public class JsonContentParserTest {
 
     @Test
     public void testUTF8Chars() throws Exception {
-        ContentElement content = TestUtils.parse(contentParser, file);
+        ContentElement content = TestUtils.parse(contentParser, new JSONParserOptions(), file);
         ContentElement child = content.getChild("jcr:content");
         assertNotNull("Expected child at jcr:content", child);
         Map<String, Object> props = child.getProperties();
@@ -154,7 +154,7 @@ public class JsonContentParserTest {
     public void testIgnoreResourcesProperties() throws Exception {
         ContentElement content = TestUtils.parse(
                 contentParser,
-                new ParserOptions().ignoreResourceNames(Collections
+                new JSONParserOptions().ignoreResourceNames(Collections
                         .unmodifiableSet(new HashSet<>(Arrays.asList("header", "newslist", "security:acl", "security:principals"))))
                         .ignorePropertyNames(Collections.unmodifiableSet(new HashSet<>(Arrays.asList("jcr:title")))), file);
         ContentElement child = content.getChild("jcr:content");
@@ -172,7 +172,7 @@ public class JsonContentParserTest {
 
     @Test
     public void testGetChild() throws Exception {
-        ContentElement content = TestUtils.parse(contentParser, file);
+        ContentElement content = TestUtils.parse(contentParser, new JSONParserOptions(), file);
         assertNull(content.getName());
         ContentElement deepChild = content.getChild("jcr:content/par/image/file/jcr:content");
         assertNotNull("Expected child at jcr:content/par/image/file/jcr:content", deepChild);
@@ -188,7 +188,7 @@ public class JsonContentParserTest {
 
     @Test(expected = ParseException.class)
     public void testFailsWithoutCommentsEnabled() throws Exception {
-        TestUtils.parse(contentParser, new ParserOptions().jsonParserFeatures(EnumSet.noneOf(JsonParserFeature.class)), file);
+        TestUtils.parse(contentParser, new JSONParserOptions().withFeatures(EnumSet.noneOf(JSONParserFeature.class)), file);
     }
 
 }