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:03:01 UTC

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 8df629a  SLING-8570 - Extract a generic Content Parser API from org.apache.sling.jcr.contentparser with pluggable implementations
8df629a is described below

commit 8df629a82a1431910aea5495658b32fbf2755172
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 +++++++++++++++++++++
 4 files changed, 332 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'
+          }
+        }
+      }
+    }
+  }
+}