resolves STREAMS-588

fixes streams-examples / .. / TwitterFollowNeo4j
by adding a way to clear zero-size objects, empty arrays, and null / empty values out of objects en-route to neo4j
diff --git a/streams-contrib/streams-persist-neo4j/src/main/java/org/apache/streams/neo4j/CypherQueryGraphHelper.java b/streams-contrib/streams-persist-neo4j/src/main/java/org/apache/streams/neo4j/CypherQueryGraphHelper.java
index 3256691..dc6efeb 100644
--- a/streams-contrib/streams-persist-neo4j/src/main/java/org/apache/streams/neo4j/CypherQueryGraphHelper.java
+++ b/streams-contrib/streams-persist-neo4j/src/main/java/org/apache/streams/neo4j/CypherQueryGraphHelper.java
@@ -124,6 +124,7 @@
     String query = createVertex.render();
 
     ObjectNode object = MAPPER.convertValue(activityObject, ObjectNode.class);
+    object = PropertyUtil.getInstance(MAPPER).cleanProperties(object);
     Map<String, Object> props = PropertyUtil.getInstance(MAPPER).flattenToMap(object);
 
     Pair<String, Map<String, Object>> queryPlusParameters = new Pair(createVertex.render(), props);
@@ -171,6 +172,7 @@
     String query = mergeVertex.render();
 
     ObjectNode object = MAPPER.convertValue(activityObject, ObjectNode.class);
+    object = PropertyUtil.getInstance(MAPPER).cleanProperties(object);
     Map<String, Object> props = PropertyUtil.getInstance(MAPPER).flattenToMap(object);
 
     LOGGER.debug("mergeVertexRequest: ({},{})", query, props);
@@ -191,6 +193,7 @@
     Pair queryPlusParameters = new Pair(null, new HashMap<>());
 
     ObjectNode object = MAPPER.convertValue(activity, ObjectNode.class);
+    object = PropertyUtil.getInstance(MAPPER).cleanProperties(object);
     Map<String, Object> props = PropertyUtil.getInstance(MAPPER).flattenToMap(object);
 
     ST mergeEdge = new ST(createEdgeStatementTemplate);
@@ -221,6 +224,7 @@
     Pair queryPlusParameters = new Pair(null, new HashMap<>());
 
     ObjectNode object = MAPPER.convertValue(activity, ObjectNode.class);
+    object = PropertyUtil.getInstance(MAPPER).cleanProperties(object);
     Map<String, Object> props = PropertyUtil.getInstance(MAPPER).flattenToMap(object);
 
     ST mergeEdge = new ST(createEdgeStatementTemplate);
diff --git a/streams-examples/streams-examples-local/pom.xml b/streams-examples/streams-examples-local/pom.xml
index 11bd321..9987df3 100644
--- a/streams-examples/streams-examples-local/pom.xml
+++ b/streams-examples/streams-examples-local/pom.xml
@@ -41,7 +41,7 @@
         <module>elasticsearch-hdfs</module>
         <module>elasticsearch-reindex</module>
         <module>mongo-elasticsearch-sync</module>
-        <!--<module>twitter-follow-neo4j</module>-->
+        <module>twitter-follow-neo4j</module>
         <module>twitter-history-elasticsearch</module>
         <module>twitter-userstream-elasticsearch</module>
     </modules>
diff --git a/streams-examples/streams-examples-local/twitter-follow-neo4j/pom.xml b/streams-examples/streams-examples-local/twitter-follow-neo4j/pom.xml
index 2e47cf6..366343d 100644
--- a/streams-examples/streams-examples-local/twitter-follow-neo4j/pom.xml
+++ b/streams-examples/streams-examples-local/twitter-follow-neo4j/pom.xml
@@ -26,8 +26,8 @@
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>streams-twitter-follow-neo4j</artifactId>
-    <name>twitter-follow-neo4j</name>
+    <artifactId>streams-example-twitter-follow-neo4j</artifactId>
+    <name>streams-example-twitter-follow-neo4j</name>
 
     <description>
         Collects friend or follower connections for a set of twitter users to build a graph database in neo4j.
@@ -188,6 +188,16 @@
                 <dependencies>
                     <dependency>
                         <groupId>org.apache.streams</groupId>
+                        <artifactId>streams-config</artifactId>
+                        <version>${project.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.streams</groupId>
+                        <artifactId>streams-runtime-local</artifactId>
+                        <version>${project.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.streams</groupId>
                         <artifactId>streams-persist-neo4j</artifactId>
                         <version>${project.version}</version>
                     </dependency>
@@ -253,4 +263,90 @@
         </plugins>
     </build>
 
+    <profiles>
+        <profile>
+            <id>dockerITs</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+                <property>
+                    <name>skipITs</name>
+                    <value>false</value>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>io.fabric8</groupId>
+                        <artifactId>docker-maven-plugin</artifactId>
+                        <configuration combine.self="override">
+                            <watchInterval>500</watchInterval>
+                            <logDate>default</logDate>
+                            <verbose>true</verbose>
+                            <autoPull>on</autoPull>
+                            <images>
+                                <image>
+                                    <name>neo4j</name>
+                                    <alias>neo4j</alias>
+                                    <run>
+                                        <env>
+                                            <NEO4J_AUTH>none</NEO4J_AUTH>
+                                        </env>
+                                        <namingStrategy>none</namingStrategy>
+                                        <ports>
+                                            <port>${http.host}:${http.port}:7474</port>
+                                            <port>${tcp.host}:${tcp.port}:7687</port>
+                                        </ports>
+                                        <portPropertyFile>neo4j.properties</portPropertyFile>
+                                        <wait>
+                                            <http>
+                                                <url>http://${http.host}:${http.port}</url>
+                                                <method>GET</method>
+                                                <status>200</status>
+                                            </http>
+                                            <time>30000</time>
+                                            <kill>1000</kill>
+                                            <shutdown>500</shutdown>
+                                        </wait>
+                                        <log>
+                                            <enabled>true</enabled>
+                                            <date>default</date>
+                                            <color>cyan</color>
+                                        </log>
+                                    </run>
+                                    <watch>
+                                        <mode>none</mode>
+                                    </watch>
+                                </image>
+
+                            </images>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>start</id>
+                                <goals>
+                                    <goal>start</goal>
+                                </goals>
+                                <phase>
+                                    pre-integration-test
+                                </phase>
+                            </execution>
+                            <execution>
+                                <id>stop</id>
+                                <goals>
+                                    <goal>stop</goal>
+                                </goals>
+                                <phase>
+                                    post-integration-test
+                                </phase>
+                            </execution>
+                        </executions>
+
+                    </plugin>
+
+                </plugins>
+            </build>
+
+        </profile>
+    </profiles>
+
 </project>
diff --git a/streams-examples/streams-examples-local/twitter-follow-neo4j/src/main/java/org/apache/streams/example/TwitterFollowNeo4j.java b/streams-examples/streams-examples-local/twitter-follow-neo4j/src/main/java/org/apache/streams/example/TwitterFollowNeo4j.java
index e268c22..03d1330 100644
--- a/streams-examples/streams-examples-local/twitter-follow-neo4j/src/main/java/org/apache/streams/example/TwitterFollowNeo4j.java
+++ b/streams-examples/streams-examples-local/twitter-follow-neo4j/src/main/java/org/apache/streams/example/TwitterFollowNeo4j.java
@@ -31,7 +31,7 @@
 import org.apache.streams.local.builders.LocalStreamBuilder;
 import org.apache.streams.neo4j.Neo4jConfiguration;
 import org.apache.streams.neo4j.bolt.Neo4jBoltPersistWriter;
-import org.apache.streams.twitter.TwitterFollowingConfiguration;
+import org.apache.streams.twitter.config.TwitterFollowingConfiguration;
 import org.apache.streams.twitter.converter.TwitterDocumentClassifier;
 import org.apache.streams.twitter.converter.TwitterFollowActivityConverter;
 import org.apache.streams.twitter.provider.TwitterFollowingProvider;
@@ -54,7 +54,7 @@
   private TwitterFollowNeo4jConfiguration config;
 
   public TwitterFollowNeo4j() {
-    this(new ComponentConfigurator<>(TwitterFollowNeo4jConfiguration.class).detectConfiguration());
+    this(new StreamsConfigurator<>(TwitterFollowNeo4jConfiguration.class).detectCustomConfiguration());
   }
 
   public TwitterFollowNeo4j(TwitterFollowNeo4jConfiguration config) {
@@ -79,9 +79,7 @@
     Neo4jBoltPersistWriter graphPersistWriter = new Neo4jBoltPersistWriter(neo4jConfiguration);
     graphPersistWriter.prepare(neo4jConfiguration);
 
-    LocalRuntimeConfiguration localRuntimeConfiguration =
-        StreamsJacksonMapper.getInstance().convertValue(StreamsConfigurator.detectConfiguration(), LocalRuntimeConfiguration.class);
-    StreamBuilder builder = new LocalStreamBuilder(localRuntimeConfiguration);
+    StreamBuilder builder = new LocalStreamBuilder();
 
     builder.newPerpetualStream(TwitterFollowingProvider.class.getCanonicalName(), followingProvider);
     builder.addStreamsProcessor(TypeConverterProcessor.class.getCanonicalName(), converter, 1, TwitterFollowingProvider.class.getCanonicalName());
diff --git a/streams-examples/streams-examples-local/twitter-follow-neo4j/src/main/jsonschema/TwitterFollowNeo4jConfiguration.json b/streams-examples/streams-examples-local/twitter-follow-neo4j/src/main/jsonschema/TwitterFollowNeo4jConfiguration.json
index e2b3386..23deacc 100644
--- a/streams-examples/streams-examples-local/twitter-follow-neo4j/src/main/jsonschema/TwitterFollowNeo4jConfiguration.json
+++ b/streams-examples/streams-examples-local/twitter-follow-neo4j/src/main/jsonschema/TwitterFollowNeo4jConfiguration.json
@@ -6,8 +6,13 @@
   "type": "object",
   "javaType" : "org.apache.streams.example.TwitterFollowNeo4jConfiguration",
   "javaInterfaces": ["java.io.Serializable"],
+  "extends": {
+    "$comment": "Absolute $ref: http://streams.apache.org/streams-runtimes/streams-runtime-local/LocalRuntimeConfiguration.json",
+    "$ref": "../../../../../../streams-runtimes/streams-runtime-local/src/main/jsonschema/LocalRuntimeConfiguration.json"
+  },
+  "additionalProperties": false,
   "properties": {
-    "twitter": { "javaType": "org.apache.streams.twitter.TwitterFollowingConfiguration", "type": "object", "required": true },
+    "twitter": { "javaType": "org.apache.streams.twitter.config.TwitterFollowingConfiguration", "type": "object", "required": true },
     "neo4j": { "javaType": "org.apache.streams.neo4j.Neo4jConfiguration", "type": "object", "required": true }
   }
 }
\ No newline at end of file
diff --git a/streams-examples/streams-examples-local/twitter-follow-neo4j/src/test/java/org/apache/streams/example/test/TwitterFollowNeo4jIT.java b/streams-examples/streams-examples-local/twitter-follow-neo4j/src/test/java/org/apache/streams/example/test/TwitterFollowNeo4jIT.java
index 75e56bb..52cb7ee 100644
--- a/streams-examples/streams-examples-local/twitter-follow-neo4j/src/test/java/org/apache/streams/example/test/TwitterFollowNeo4jIT.java
+++ b/streams-examples/streams-examples-local/twitter-follow-neo4j/src/test/java/org/apache/streams/example/test/TwitterFollowNeo4jIT.java
@@ -19,6 +19,7 @@
 package org.apache.streams.example.test;
 
 import org.apache.streams.config.ComponentConfigurator;
+import org.apache.streams.config.StreamsConfigurator;
 import org.apache.streams.core.StreamsResultSet;
 import org.apache.streams.example.TwitterFollowNeo4j;
 import org.apache.streams.example.TwitterFollowNeo4jConfiguration;
@@ -43,8 +44,11 @@
 import java.io.File;
 import java.io.IOException;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 import static org.testng.Assert.assertTrue;
-
 /**
  * TwitterFollowNeo4jIT is an integration test for TwitterFollowNeo4j.
  */
@@ -63,12 +67,7 @@
   @BeforeClass
   public void prepareTest() throws IOException {
 
-    Config reference  = ConfigFactory.load();
-    File conf = new File("target/test-classes/TwitterFollowNeo4jIT.conf");
-    assertTrue(conf.exists());
-    Config testResourceConfig  = ConfigFactory.parseFileAnySyntax(conf, ConfigParseOptions.defaults().setAllowMissing(false));
-    Config typesafe  = testResourceConfig.withFallback(reference).resolve();
-    testConfiguration = new ComponentConfigurator<>(TwitterFollowNeo4jConfiguration.class).detectConfiguration(typesafe);
+    testConfiguration = new StreamsConfigurator<>(TwitterFollowNeo4jConfiguration.class).detectCustomConfiguration();
     testClient = Neo4jBoltClient.getInstance(testConfiguration.getNeo4j());
 
     Session session = testClient.client().session();
@@ -89,18 +88,18 @@
     Neo4jReaderConfiguration vertexReaderConfiguration= MAPPER.convertValue(testConfiguration.getNeo4j(), Neo4jReaderConfiguration.class);
     vertexReaderConfiguration.setQuery("MATCH (v) return v");
     Neo4jBoltPersistReader vertexReader = new Neo4jBoltPersistReader(vertexReaderConfiguration);
-    vertexReader.prepare(null);
+    vertexReader.prepare(vertexReaderConfiguration);
     StreamsResultSet vertexResultSet = vertexReader.readAll();
     LOGGER.info("Total Read: {}", vertexResultSet.size() );
-    assertTrue(vertexResultSet.size() > 100);
+    assertThat(vertexResultSet.size(), greaterThan(100));
 
     Neo4jReaderConfiguration edgeReaderConfiguration= MAPPER.convertValue(testConfiguration.getNeo4j(), Neo4jReaderConfiguration.class);
     edgeReaderConfiguration.setQuery("MATCH (s)-[r]->(d) return r");
     Neo4jBoltPersistReader edgeReader = new Neo4jBoltPersistReader(edgeReaderConfiguration);
-    edgeReader.prepare(null);
+    edgeReader.prepare(edgeReaderConfiguration);
     StreamsResultSet edgeResultSet = edgeReader.readAll();
     LOGGER.info("Total Read: {}", edgeResultSet.size() );
-    assertTrue(edgeResultSet.size() == vertexResultSet.size()-1);
+    assertThat(edgeResultSet.size(), equalTo(vertexResultSet.size()-1));
 
   }
 
diff --git a/streams-examples/streams-examples-local/twitter-follow-neo4j/src/test/resources/TwitterFollowNeo4jIT.conf b/streams-examples/streams-examples-local/twitter-follow-neo4j/src/test/resources/TwitterFollowNeo4jIT.conf
deleted file mode 100644
index d54a50c..0000000
--- a/streams-examples/streams-examples-local/twitter-follow-neo4j/src/test/resources/TwitterFollowNeo4jIT.conf
+++ /dev/null
@@ -1,32 +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
-#
-#   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.
-include "../../../../../twitter.conf"
-include "../../../../../neo4j.properties"
-twitter {
-  debug = true
-  ids_only = false
-  endpoint = "friends"
-  info = [
-    18055613
-  ]
-  twitter.max_items = 200
-}
-neo4j {
-  scheme = "tcp"
-  hosts += ${neo4j.tcp.host}
-  port = ${neo4j.tcp.port}
-}
-taskTimeoutMs = 60000
\ No newline at end of file
diff --git a/streams-util/src/main/java/org/apache/streams/util/PropertyUtil.java b/streams-util/src/main/java/org/apache/streams/util/PropertyUtil.java
index 63678f6..4ea46fd 100644
--- a/streams-util/src/main/java/org/apache/streams/util/PropertyUtil.java
+++ b/streams-util/src/main/java/org/apache/streams/util/PropertyUtil.java
@@ -27,6 +27,7 @@
 import com.github.wnameless.json.flattener.JsonFlattener;
 import com.github.wnameless.json.unflattener.JsonUnflattener;
 import org.apache.commons.lang3.StringUtils;
+import org.codehaus.jackson.JsonFactory;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -140,6 +141,42 @@
     return merged;
   }
 
+  public ObjectNode cleanProperties(ObjectNode content) {
+    return cleanProperties(mapper, content);
+  }
+
+  public static ObjectNode cleanProperties(ObjectMapper mapper, ObjectNode content) {
+
+    ObjectNode cleaned = mapper.createObjectNode();
+
+    Iterator<Map.Entry<String, JsonNode>> fields = content.fields();
+    for ( ; fields.hasNext(); ) {
+      Map.Entry<String, JsonNode> field = fields.next();
+      String fieldId = field.getKey();
+      if( field.getValue() != null && !field.getValue().getNodeType().equals(JsonNodeType.NULL)) {
+        if( field.getValue().getNodeType().equals(JsonNodeType.OBJECT)) {
+          ObjectNode clean = cleanProperties(mapper, field.getValue().deepCopy());
+          if( clean != null && clean.size() > 0 ) {
+            cleaned.put(fieldId, clean);
+          }
+        } else if ( field.getValue().getNodeType().equals(JsonNodeType.ARRAY)) {
+          ArrayNode clean = cleanArray((ArrayNode)field.getValue());
+          if( clean != null && clean.size() > 0 ) {
+            cleaned.put(fieldId, clean);
+          }
+        } else if ( field.getValue().getNodeType().equals(JsonNodeType.STRING)) {
+          String value = content.get(fieldId).asText();
+          if( value != null && StringUtils.isNotBlank(value)) {
+            cleaned.put(fieldId, value);
+          }
+        } else {
+          cleaned.put(fieldId, field.getValue());
+        }
+      }
+    }
+    return cleaned;
+  }
+
   /**
    * merge two arrays.
    * @param content ArrayNode
@@ -150,4 +187,26 @@
     return parent.deepCopy().addAll(content);
   }
 
+  /**
+   * clean an array.
+   * @param content ArrayNode
+   * @return cleaned ArrayNode
+   */
+  private static ArrayNode cleanArray(ArrayNode content) {
+    if( content.size() == 0) return null;
+    Iterator<JsonNode> items = content.iterator();
+    for ( ; items.hasNext(); ) {
+      JsonNode item = items.next();
+      if( item == null ) items.remove();
+      if( item.getNodeType().equals(JsonNodeType.OBJECT)) {
+        if( item.size() == 0 ) items.remove();
+      }
+      if( item.getNodeType().equals(JsonNodeType.ARRAY)) {
+        if( item.size() == 0 ) items.remove();
+      }
+    }
+    if( content.size() == 0) return null;
+    else return content;
+  }
+
 }
diff --git a/streams-util/src/test/java/org/apache/streams/util/test/PropertyUtilTest.java b/streams-util/src/test/java/org/apache/streams/util/test/PropertyUtilTest.java
index fdba3f0..ae292dd 100644
--- a/streams-util/src/test/java/org/apache/streams/util/test/PropertyUtilTest.java
+++ b/streams-util/src/test/java/org/apache/streams/util/test/PropertyUtilTest.java
@@ -19,14 +19,20 @@
 package org.apache.streams.util.test;
 
 import org.apache.streams.util.PropertyUtil;
-import org.apache.streams.util.schema.SchemaUtil;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.junit.Test;
 
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.testng.Assert.assertTrue;
+
 /**
  * Unit Test for PropertyUtil.
  */
@@ -37,20 +43,39 @@
   String flatJson = "{\"a.b\": \"ab\", \"c.d\": \"cd\", \"a.e\": \"ae\", \"c.f\": \"cf\"}";
 
   @Test
+  public void testFlattenToMap() throws Exception {
+    PropertyUtil propertyUtil = PropertyUtil.getInstance();
+    ObjectMapper mapper = new ObjectMapper();
+    ObjectNode node = mapper.createObjectNode();
+    node.put("objectType", "post");
+    node.putArray("links").add("link1");
+    node.putObject("actor").put("id", "leftId").putObject("author").put("id", "authorId");
+    node.putObject("extensions");
+    Map flattenedMap = propertyUtil.flattenToMap(node);
+    assertThat(flattenedMap.size(), equalTo(5));
+    assertThat(flattenedMap.get("actor.id"), notNullValue());
+    assertThat(flattenedMap.get("actor.author.id"), notNullValue());
+    assertThat(flattenedMap.get("objectType"), equalTo("post"));
+    assertThat(flattenedMap.get("links"), nullValue());
+    assertThat(flattenedMap.get("links[0]"), notNullValue());
+    assertThat(flattenedMap.get("extensions"), notNullValue());
+  }
+
+  @Test
   public void testUnflattenObjectNode() throws Exception {
     PropertyUtil propertyUtil = PropertyUtil.getInstance();
     ObjectNode flatNode = mapper.readValue(flatJson, ObjectNode.class);
     ObjectNode unflattenedNode = propertyUtil.unflattenObjectNode(flatNode);
-    assert(unflattenedNode.size() == 2);
-    assert(unflattenedNode.get("a") != null);
-    assert(unflattenedNode.get("b") == null);
-    assert(unflattenedNode.get("c") != null);
-    assert(unflattenedNode.get("a").size() == 2);
-    assert(unflattenedNode.get("a").get("b").asText().equals("ab"));
-    assert(unflattenedNode.get("a").get("e").asText().equals("ae"));
-    assert(unflattenedNode.get("c").size() == 2);
-    assert(unflattenedNode.get("c").get("d").asText().equals("cd"));
-    assert(unflattenedNode.get("c").get("f").asText().equals("cf"));
+    assertThat(unflattenedNode.size(), equalTo(2));
+    assertThat(unflattenedNode.get("a"), notNullValue());
+    assertThat(unflattenedNode.get("b"), nullValue());
+    assertThat(unflattenedNode.get("c"), notNullValue());
+    assertThat(unflattenedNode.get("a").size(), equalTo(2));
+    assertThat(unflattenedNode.get("a").get("b").asText(), equalTo("ab"));
+    assertThat(unflattenedNode.get("a").get("e").asText(), equalTo("ae"));
+    assertThat(unflattenedNode.get("c").size(), equalTo(2));
+    assertThat(unflattenedNode.get("c").get("d").asText(), equalTo("cd"));
+    assertThat(unflattenedNode.get("c").get("f").asText(), equalTo("cf"));
   }
 
   @Test
@@ -64,17 +89,44 @@
     right.putArray("links").add("link2");
     right.putObject("actor").put("id", "rightId").putObject("image").put("url", "http://url.com");
     JsonNode merged = PropertyUtil.mergeProperties(left, right);
-    assert( merged.has("objectType"));
-    assert( merged.has("actor"));
-    assert( merged.has("links"));
-    assert( (merged.get("links")).size() == 2);
-    assert( merged.get("actor").has("id"));
-    assert( merged.get("actor").get("id").asText().equals("leftId"));
-    assert( merged.get("actor").has("author"));
-    assert( merged.get("actor").get("author").has("id"));
-    assert( merged.get("actor").has("image"));
-    assert( merged.get("actor").get("image").has("url"));
+    assertTrue(merged.has("objectType"));
+    assertTrue(merged.has("actor"));
+    assertTrue(merged.has("links"));
+    assertThat((merged.get("links")).size(), equalTo(2));
+    assertTrue(merged.get("actor").has("id"));
+    assertThat(merged.get("actor").get("id").asText(), equalTo("leftId"));
+    assertTrue(merged.get("actor").has("author"));
+    assertTrue(merged.get("actor").get("author").has("id"));
+    assertTrue(merged.get("actor").has("image"));
+    assertTrue(merged.get("actor").get("image").has("url"));
 
   }
+
+  @Test
+  public void testCleanProperties() throws Exception {
+    PropertyUtil propertyUtil = PropertyUtil.getInstance();
+    ObjectMapper mapper = new ObjectMapper();
+    ObjectNode node = mapper.createObjectNode();
+    String nullid = null;
+    node.put("id", nullid);
+    node.put("objectType", "post");
+    node.putArray("links").add("link1");
+    node.putArray("urls");
+    node.putObject("actor").put("id", "leftId").putObject("author").put("id", "authorId");
+    node.putObject("object").put("id", "").put("name", "O");
+    node.putObject("target").put("id", "");
+    node.putObject("extensions");
+    ObjectNode cleaned = propertyUtil.cleanProperties(node);
+    assertThat(cleaned.has("id"), equalTo(false));
+    assertThat(cleaned.has("objectType"), equalTo(true));
+    assertThat(cleaned.has("links"), equalTo(true));
+    assertThat(cleaned.has("urls"), equalTo(false));
+    assertThat(cleaned.has("actor"), equalTo(true));
+    assertThat(cleaned.get("actor").size(), equalTo(2));
+    assertThat(cleaned.has("object"), equalTo(true));
+    assertThat(cleaned.get("object").size(), equalTo(1));
+    assertThat(cleaned.has("target"), equalTo(false));
+    assertThat(cleaned.has("extensions"), equalTo(false));
+  }
 }