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/24 13:04:56 UTC

[sling-org-apache-sling-contentparser-json] branch master updated (8df629a -> ca5ec09)

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.


 discard 8df629a  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
     new ca5ec09  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (8df629a)
            \
             N -- N -- N   refs/heads/master (ca5ec09)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 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.


Summary of changes:
 src/test/resources/invalid-test/invalid-ticks.json | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 src/test/resources/invalid-test/invalid-ticks.json


[sling-org-apache-sling-contentparser-json] 01/01: 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 ca5ec09f01291cffac1e37a1e30391058c805835
Author: Radu Cotescu <co...@adobe.com>
AuthorDate: Wed Jul 24 15:02:03 2019 +0200

    SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
    
    * removed the ParseException and replaced it with IOException
    * minor code cleanup
    * improved code coverage
---
 .../contentparser/json/JSONParserOptions.java      |  12 +-
 .../json/internal/JSONContentParser.java           |  50 ++--
 .../json/internal/JSONContentParserTest.java       |  33 ++-
 src/test/resources/content-test/content-ticks.json | 274 +++++++++++++++++++++
 src/test/resources/invalid-test/invalid-ticks.json |   3 +
 5 files changed, 335 insertions(+), 37 deletions(-)

diff --git a/src/main/java/org/apache/sling/contentparser/json/JSONParserOptions.java b/src/main/java/org/apache/sling/contentparser/json/JSONParserOptions.java
index 0fe1ac0..3d4ea92 100644
--- a/src/main/java/org/apache/sling/contentparser/json/JSONParserOptions.java
+++ b/src/main/java/org/apache/sling/contentparser/json/JSONParserOptions.java
@@ -19,7 +19,9 @@
 package org.apache.sling.contentparser.json;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.EnumSet;
+import java.util.Set;
 
 import org.apache.sling.contentparser.api.ParserOptions;
 import org.osgi.annotation.versioning.ConsumerType;
@@ -34,10 +36,10 @@ 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);
+    public static final Set<JSONParserFeature> DEFAULT_JSON_PARSER_FEATURES =
+            Collections.unmodifiableSet(EnumSet.of(JSONParserFeature.COMMENTS));
 
-    private EnumSet<JSONParserFeature> features = DEFAULT_JSON_PARSER_FEATURES;
+    private Set<JSONParserFeature> features = DEFAULT_JSON_PARSER_FEATURES;
 
     /**
      * Set the features the JSON parser should apply when parsing files.
@@ -45,7 +47,7 @@ public final class JSONParserOptions extends ParserOptions {
      * @param value JSON parser features
      * @return this
      */
-    public JSONParserOptions withFeatures(EnumSet<JSONParserFeature> value) {
+    public JSONParserOptions withFeatures(Set<JSONParserFeature> value) {
         this.features = EnumSet.copyOf(value);
         return this;
     }
@@ -67,7 +69,7 @@ public final class JSONParserOptions extends ParserOptions {
      *
      * @return the features the JSON parser should apply when parsing files
      */
-    public EnumSet<JSONParserFeature> getFeatures() {
+    public Set<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
index 831ee22..798b62f 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,7 +42,6 @@ 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.ParseException;
 import org.apache.sling.contentparser.api.ParserHelper;
 import org.apache.sling.contentparser.api.ParserOptions;
 import org.apache.sling.contentparser.json.JSONParserFeature;
@@ -56,8 +55,10 @@ import org.osgi.service.component.annotations.Component;
 )
 public class JSONContentParser implements ContentParser {
 
+    private static final String JOHNZON_SUPPORT_COMMENTS = "org.apache.johnzon.supports-comments";
+
     @Override
-    public void parse(ContentHandler handler, InputStream is, ParserOptions parserOptions) throws ParseException {
+    public void parse(ContentHandler handler, InputStream is, ParserOptions parserOptions) throws IOException {
         final boolean jsonQuoteTicks;
         final boolean supportComments;
         if (parserOptions instanceof JSONParserOptions) {
@@ -74,33 +75,28 @@ public class JSONContentParser implements ContentParser {
          * 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(
-                        supportComments ?
-                                new HashMap<String, Object>() {{
-                                    put("org.apache.johnzon.supports-comments", true);
-                                }} :
-                                Collections.emptyMap()
-                );
+        Map<String, Object> jsonReaderFactoryConfiguration;
+        if (supportComments) {
+            jsonReaderFactoryConfiguration = new HashMap<>();
+            jsonReaderFactoryConfiguration.put(JOHNZON_SUPPORT_COMMENTS, true);
+        } else {
+            jsonReaderFactoryConfiguration = Collections.emptyMap();
+        }
+        final JsonReaderFactory jsonReaderFactory = Json.createReaderFactory(jsonReaderFactoryConfiguration);
         JsonObject jsonObject = jsonQuoteTicks ? toJsonObjectWithJsonTicks(jsonReaderFactory, is) : toJsonObject(jsonReaderFactory, is);
         parse(handler, jsonObject, parserOptions, "/");
     }
 
-    private JsonObject toJsonObject(JsonReaderFactory jsonReaderFactory, InputStream is) {
+    private JsonObject toJsonObject(JsonReaderFactory jsonReaderFactory, InputStream is) throws IOException {
         try (JsonReader reader = jsonReaderFactory.createReader(is)) {
             return reader.readObject();
         } catch (JsonParsingException ex) {
-            throw new ParseException("Error parsing JSON content: " + ex.getMessage(), ex);
+            throw new IOException("Error parsing JSON content.", ex);
         }
     }
 
-    private JsonObject toJsonObjectWithJsonTicks(JsonReaderFactory jsonReaderFactory, InputStream is) {
-        String jsonString;
-        try {
-            jsonString = IOUtils.toString(is, StandardCharsets.UTF_8);
-        } catch (IOException ex) {
-            throw new ParseException("Error getting JSON string.", ex);
-        }
+    private JsonObject toJsonObjectWithJsonTicks(JsonReaderFactory jsonReaderFactory, InputStream is) throws IOException {
+        String jsonString = IOUtils.toString(is, StandardCharsets.UTF_8);
 
         // convert ticks to double quotes
         jsonString = JSONTicksConverter.tickToDoubleQuote(jsonString);
@@ -108,11 +104,11 @@ public class JSONContentParser implements ContentParser {
         try (JsonReader reader = jsonReaderFactory.createReader(new StringReader(jsonString))) {
             return reader.readObject();
         } catch (JsonParsingException ex) {
-            throw new ParseException("Error parsing JSON content: " + ex.getMessage(), ex);
+            throw new IOException("Error parsing JSON content.", ex);
         }
     }
 
-    private void parse(ContentHandler handler, JsonObject object, ParserOptions parserOptions, String path) {
+    private void parse(ContentHandler handler, JsonObject object, ParserOptions parserOptions, String path) throws IOException {
         // parse JSON object
         Map<String, Object> properties = new HashMap<>();
         Map<String, JsonObject> children = new LinkedHashMap<>();
@@ -122,12 +118,12 @@ public class JSONContentParser implements ContentParser {
             boolean ignore = false;
             try {
                 value = convertValue(parserOptions, entry.getValue());
-            } catch (ParseException ex) {
+            } catch (IllegalArgumentException ex) {
                 if (parserOptions.getIgnoreResourceNames().contains(childName) || parserOptions.getIgnorePropertyNames()
                         .contains(removePrefixFromPropertyName(parserOptions.getRemovePropertyNamePrefixes(), childName))) {
                     ignore = true;
                 } else {
-                    throw ex;
+                    throw new IOException(ex);
                 }
             }
             boolean isResource = (value instanceof JsonObject);
@@ -154,10 +150,8 @@ public class JSONContentParser implements ContentParser {
             }
         }
         String defaultPrimaryType = parserOptions.getDefaultPrimaryType();
-        if (defaultPrimaryType != null) {
-            if (!properties.containsKey("jcr:primaryType")) {
-                properties.put("jcr:primaryType", defaultPrimaryType);
-            }
+        if (defaultPrimaryType != null && !properties.containsKey("jcr:primaryType")) {
+            properties.put("jcr:primaryType", defaultPrimaryType);
         }
 
         // report current JSON object
@@ -204,7 +198,7 @@ public class JSONContentParser implements ContentParser {
             case OBJECT:
                 return value;
             default:
-                throw new ParseException("Unexpected JSON value type: " + value.getValueType());
+                throw new IllegalArgumentException("Unexpected JSON value type: " + value.getValueType());
         }
     }
 
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 658151d..48a5c3e 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
@@ -19,6 +19,7 @@
 package org.apache.sling.contentparser.json.internal;
 
 import java.io.File;
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -29,7 +30,6 @@ import java.util.Map;
 import java.util.TimeZone;
 
 import org.apache.sling.contentparser.api.ContentParser;
-import org.apache.sling.contentparser.api.ParseException;
 import org.apache.sling.contentparser.json.JSONParserFeature;
 import org.apache.sling.contentparser.json.JSONParserOptions;
 import org.apache.sling.contentparser.testutils.TestUtils;
@@ -136,14 +136,14 @@ public class JSONContentParserTest {
         assertEquals("äöü߀", props.get("utf8Property"));
     }
 
-    @Test(expected = ParseException.class)
+    @Test(expected = IOException.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)
+    @Test(expected = IOException.class)
     public void testParseInvalidJsonWithObjectList() throws Exception {
         file = new File("src/test/resources/invalid-test/contentWithObjectList.json");
         ContentElement content = TestUtils.parse(contentParser, file);
@@ -186,9 +186,34 @@ public class JSONContentParserTest {
         assertNull(invalidChild);
     }
 
-    @Test(expected = ParseException.class)
+    @Test
+    public void testGetChildFromJsonWithTicks() throws Exception {
+        File jsonWithTicks = file = new File("src/test/resources/content-test/content-ticks.json");
+        ContentElement content = TestUtils
+                .parse(contentParser, new JSONParserOptions().withFeatures(JSONParserFeature.COMMENTS, JSONParserFeature.QUOTE_TICK),
+                        jsonWithTicks);
+        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"));
+
+        ContentElement invalidChild = content.getChild("non/existing/path");
+        assertNull(invalidChild);
+
+        invalidChild = content.getChild("/jcr:content");
+        assertNull(invalidChild);
+    }
+
+    @Test(expected = IOException.class)
     public void testFailsWithoutCommentsEnabled() throws Exception {
         TestUtils.parse(contentParser, new JSONParserOptions().withFeatures(EnumSet.noneOf(JSONParserFeature.class)), file);
     }
 
+    @Test(expected = IOException.class)
+    public void testInvalidJsonWithTicks() throws Exception {
+        TestUtils.parse(contentParser, new JSONParserOptions().withFeatures(EnumSet.noneOf(JSONParserFeature.class)), new File("src/test" +
+                "/resources/content-test/invalid-ticks.json"));
+    }
+
 }
diff --git a/src/test/resources/content-test/content-ticks.json b/src/test/resources/content-test/content-ticks.json
new file mode 100644
index 0000000..96a2c41
--- /dev/null
+++ b/src/test/resources/content-test/content-ticks.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< [...]
+        '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/invalid-ticks.json b/src/test/resources/invalid-test/invalid-ticks.json
new file mode 100644
index 0000000..f45e407
--- /dev/null
+++ b/src/test/resources/invalid-test/invalid-ticks.json
@@ -0,0 +1,3 @@
+{
+    'hello': world
+}