Move all json handling methods into base class
diff --git a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
index 116ec26..9dea9bc 100644
--- a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
+++ b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
@@ -19,23 +19,14 @@
 import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.function.BiConsumer;
 
 import javax.json.Json;
 import javax.json.JsonObject;
 import javax.json.stream.JsonParsingException;
 
-import org.apache.felix.utils.resource.CapabilityImpl;
-import org.apache.felix.utils.resource.RequirementImpl;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.Include;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
 
 /**
  * This class offers a method to read a {@code Feature} using a {@code Reader} instance.
@@ -104,9 +95,9 @@
         this.readFrameworkProperties(map, feature.getFrameworkProperties());
         this.readConfigurations(map, feature.getConfigurations());
 
-        this.readCapabilities(map);
-        this.readRequirements(map);
-        this.readIncludes(map);
+        this.readCapabilities(map, feature.getCapabilities());
+        this.readRequirements(map, feature.getRequirements());
+        feature.setInclude(this.readInclude(map));
 
         this.readExtensions(map,
                 JSONConstants.FEATURE_KNOWN_PROPERTIES,
@@ -124,197 +115,6 @@
             throw new IOException("Unsupported model version: " + modelVersion);
         }
     }
-
-    private void readIncludes(final Map<String, Object> map) throws IOException {
-        if ( map.containsKey(JSONConstants.FEATURE_INCLUDE)) {
-            final Object includeObj = map.get(JSONConstants.FEATURE_INCLUDE);
-            checkType(JSONConstants.FEATURE_INCLUDE, includeObj, Map.class, String.class);
-
-            @SuppressWarnings("unchecked")
-            final Include include;
-            if ( includeObj instanceof String ) {
-                final ArtifactId id = ArtifactId.parse(includeObj.toString());
-                include = new Include(id);
-            } else {
-                @SuppressWarnings("unchecked")
-                final Map<String, Object> obj = (Map<String, Object>) includeObj;
-                if ( !obj.containsKey(JSONConstants.ARTIFACT_ID) ) {
-                    throw new IOException(exceptionPrefix + " include is missing required artifact id");
-                }
-                checkType("Include " + JSONConstants.ARTIFACT_ID, obj.get(JSONConstants.ARTIFACT_ID), String.class);
-                final ArtifactId id = ArtifactId.parse(obj.get(JSONConstants.ARTIFACT_ID).toString());
-                include = new Include(id);
-
-                if ( obj.containsKey(JSONConstants.INCLUDE_REMOVALS) ) {
-                    checkType("Include removals", obj.get(JSONConstants.INCLUDE_REMOVALS), Map.class);
-                    @SuppressWarnings("unchecked")
-                    final Map<String, Object> removalObj = (Map<String, Object>) obj.get(JSONConstants.INCLUDE_REMOVALS);
-                    if ( removalObj.containsKey(JSONConstants.FEATURE_BUNDLES) ) {
-                        checkType("Include removal bundles", removalObj.get(JSONConstants.FEATURE_BUNDLES), List.class);
-                        @SuppressWarnings("unchecked")
-                        final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_BUNDLES);
-                        for(final Object val : list) {
-                            checkType("Include removal bundles", val, String.class);
-                            if ( val.toString().startsWith("#")) {
-                                continue;
-                            }
-                            include.getBundleRemovals().add(ArtifactId.parse(val.toString()));
-                        }
-                    }
-                    if ( removalObj.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
-                        checkType("Include removal configuration", removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS), List.class);
-                        @SuppressWarnings("unchecked")
-                        final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS);
-                        for(final Object val : list) {
-                            checkType("Include removal configuration", val, String.class);
-                            include.getConfigurationRemovals().add(val.toString());
-                        }
-                    }
-                    if ( removalObj.containsKey(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES) ) {
-                        checkType("Include removal framework properties", removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES), List.class);
-                        @SuppressWarnings("unchecked")
-                        final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
-                        for(final Object val : list) {
-                            checkType("Include removal framework properties", val, String.class);
-                            include.getFrameworkPropertiesRemovals().add(val.toString());
-                        }
-                    }
-                    if ( removalObj.containsKey(JSONConstants.INCLUDE_EXTENSION_REMOVALS) ) {
-                        checkType("Include removal extensions", removalObj.get(JSONConstants.INCLUDE_EXTENSION_REMOVALS), List.class);
-                        @SuppressWarnings("unchecked")
-                        final List<Object> list = (List<Object>)removalObj.get(JSONConstants.INCLUDE_EXTENSION_REMOVALS);
-                        for(final Object val : list) {
-                            checkType("Include removal extension", val, String.class, Map.class);
-                            if ( val instanceof String ) {
-                                if ( val.toString().startsWith("#")) {
-                                    continue;
-                                }
-                                include.getExtensionRemovals().add(val.toString());
-                            } else {
-                                @SuppressWarnings("unchecked")
-                                final Map<String, Object> removalMap = (Map<String, Object>)val;
-                                final Object nameObj = removalMap.get("name");
-                                checkType("Include removal extension", nameObj, String.class);
-                                if ( removalMap.containsKey("artifacts") ) {
-                                    checkType("Include removal extension artifacts", removalMap.get("artifacts"), List.class);
-                                    @SuppressWarnings("unchecked")
-                                    final List<Object> artifactList = (List<Object>)removalMap.get("artifacts");
-                                    final List<ArtifactId> ids = new ArrayList<>();
-                                    for(final Object aid : artifactList) {
-                                        checkType("Include removal extension artifact", aid, String.class);
-                                        ids.add(ArtifactId.parse(aid.toString()));
-                                    }
-                                    include.getArtifactExtensionRemovals().put(nameObj.toString(), ids);
-                                } else {
-                                    include.getExtensionRemovals().add(nameObj.toString());
-                                }
-                            }
-                        }
-                    }
-
-                }
-            }
-            feature.setInclude(include);
-        }
-    }
-
-    private void readRequirements(Map<String, Object> map) throws IOException {
-        if ( map.containsKey(JSONConstants.FEATURE_REQUIREMENTS)) {
-            final Object reqObj = map.get(JSONConstants.FEATURE_REQUIREMENTS);
-            checkType(JSONConstants.FEATURE_REQUIREMENTS, reqObj, List.class);
-
-            @SuppressWarnings("unchecked")
-            final List<Object> requirements = (List<Object>)reqObj;
-            for(final Object req : requirements) {
-                checkType("Requirement", req, Map.class);
-                @SuppressWarnings("unchecked")
-                final Map<String, Object> obj = (Map<String, Object>) req;
-
-                if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
-                    throw new IOException(this.exceptionPrefix + "Namespace is missing for requirement");
-                }
-                checkType("Requirement namespace", obj.get(JSONConstants.REQCAP_NAMESPACE), String.class);
-
-                Map<String, Object> attrMap = new HashMap<>();
-                if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
-                    checkType("Requirement attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
-                    @SuppressWarnings("unchecked")
-                    final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
-                    attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
-                }
-
-                Map<String, String> dirMap = new HashMap<>();
-                if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
-                    checkType("Requirement directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
-                    @SuppressWarnings("unchecked")
-                    final Map<String, Object> dirs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_DIRECTIVES);
-                    dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
-                }
-
-                final Requirement r = new RequirementImpl(null, obj.get(JSONConstants.REQCAP_NAMESPACE).toString(), dirMap, attrMap);
-                feature.getRequirements().add(r);
-            }
-        }
-    }
-
-    private void readCapabilities(Map<String, Object> map) throws IOException {
-        if ( map.containsKey(JSONConstants.FEATURE_CAPABILITIES)) {
-            final Object capObj = map.get(JSONConstants.FEATURE_CAPABILITIES);
-            checkType(JSONConstants.FEATURE_CAPABILITIES, capObj, List.class);
-
-            @SuppressWarnings("unchecked")
-            final List<Object> capabilities = (List<Object>)capObj;
-            for(final Object cap : capabilities) {
-                checkType("Capability", cap, Map.class);
-                @SuppressWarnings("unchecked")
-                final Map<String, Object> obj = (Map<String, Object>) cap;
-
-                if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
-                    throw new IOException(this.exceptionPrefix + "Namespace is missing for capability");
-                }
-                checkType("Capability namespace", obj.get(JSONConstants.REQCAP_NAMESPACE), String.class);
-
-                Map<String, Object> attrMap = new HashMap<>();
-                if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
-                    checkType("Capability attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
-                    @SuppressWarnings("unchecked")
-                    final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
-                    attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
-                }
-
-                Map<String, String> dirMap = new HashMap<>();
-                if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
-                    checkType("Capability directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
-                    @SuppressWarnings("unchecked")
-                    final Map<String, Object> dirs = (Map<String, Object>) obj.get(JSONConstants.REQCAP_DIRECTIVES);
-                    dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
-                }
-
-                final Capability c = new CapabilityImpl(null, obj.get(JSONConstants.REQCAP_NAMESPACE).toString(), dirMap, attrMap);
-                feature.getCapabilities().add(c);
-            }
-        }
-    }
-
-    @FunctionalInterface
-    private interface BiConsumer_WithExceptions<T, V, E extends Exception> {
-        void accept(T t, V u) throws E;
-    }
-
-    private static <T, V, E extends Exception> BiConsumer<T, V> rethrowBiConsumer(BiConsumer_WithExceptions<T, V, E> biConsumer) {
-        return (t, u) -> {
-            try {
-                biConsumer.accept(t, u);
-            } catch (Exception exception) {
-                throwAsUnchecked(exception);
-            }
-        };
-    }
-
-    @SuppressWarnings ("unchecked")
-    private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
-        throw (E) exception;
-    }
 }
 
 
diff --git a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
index 163a164..a7cd9a1 100644
--- a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
+++ b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
@@ -18,19 +18,12 @@
 
 import java.io.IOException;
 import java.io.Writer;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
 
 import javax.json.stream.JsonGenerator;
 
-import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
 import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.Include;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
 
 /**
  * Simple JSON writer for a feature
@@ -51,116 +44,6 @@
         w.writeFeature(writer, feature);
     }
 
-    private void writeProperty(final JsonGenerator generator, final String key, final String value) {
-        if ( value != null ) {
-            generator.write(key, value);
-        }
-    }
-
-    private <T> void writeList(final JsonGenerator generator, final String name, final Collection<T> values) {
-        if (!values.isEmpty()) {
-            generator.writeStartArray(name);
-            for (T value : values) {
-                generator.write(value.toString());
-            }
-            generator.writeEnd();
-        }
-    }
-
-    private void writeInclude(final JsonGenerator generator, final Include inc) {
-        if (inc == null) {
-            return;
-        }
-
-        if ( inc.getArtifactExtensionRemovals().isEmpty()
-             && inc.getBundleRemovals().isEmpty()
-             && inc.getConfigurationRemovals().isEmpty()
-             && inc.getFrameworkPropertiesRemovals().isEmpty() ) {
-
-            generator.write(JSONConstants.FEATURE_INCLUDE, inc.getId().toMvnId());
-        } else {
-            generator.writeStartObject(JSONConstants.FEATURE_INCLUDE);
-            writeProperty(generator, JSONConstants.ARTIFACT_ID, inc.getId().toMvnId());
-
-            generator.writeStartObject(JSONConstants.INCLUDE_REMOVALS);
-
-            if ( !inc.getArtifactExtensionRemovals().isEmpty()
-                 || inc.getExtensionRemovals().isEmpty() ) {
-                generator.writeStartArray(JSONConstants.INCLUDE_EXTENSION_REMOVALS);
-
-                for(final String id : inc.getExtensionRemovals()) {
-                    generator.write(id);
-                }
-                for(final Map.Entry<String, List<ArtifactId>> entry : inc.getArtifactExtensionRemovals().entrySet()) {
-                    generator.writeStartObject();
-
-                    writeList(generator, entry.getKey(), entry.getValue());
-
-                    generator.writeEnd();
-                }
-
-                generator.writeEnd();
-            }
-            writeList(generator, JSONConstants.FEATURE_CONFIGURATIONS, inc.getConfigurationRemovals());
-            writeList(generator, JSONConstants.FEATURE_BUNDLES, inc.getBundleRemovals());
-            writeList(generator, JSONConstants.FEATURE_FRAMEWORK_PROPERTIES, inc.getFrameworkPropertiesRemovals());
-
-            generator.writeEnd().writeEnd();
-        }
-    }
-
-    private void writeRequirements(final JsonGenerator generator, final List<Requirement> requirements) {
-        if (requirements.isEmpty()) {
-            return;
-        }
-
-        generator.writeStartArray(JSONConstants.FEATURE_REQUIREMENTS);
-
-        for(final Requirement req : requirements) {
-            generator.writeStartObject();
-            writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, req.getNamespace());
-            if ( !req.getAttributes().isEmpty() ) {
-                generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
-                req.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
-                generator.writeEnd();
-            }
-            if ( !req.getDirectives().isEmpty() ) {
-                generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
-                req.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
-                generator.writeEnd();
-            }
-            generator.writeEnd();
-        }
-
-        generator.writeEnd();
-    }
-
-    private void writeCapabilities(final JsonGenerator generator, final List<Capability> capabilities) {
-        if (capabilities.isEmpty()) {
-            return;
-        }
-
-        generator.writeStartArray(JSONConstants.FEATURE_CAPABILITIES);
-
-        for(final Capability cap : capabilities) {
-            generator.writeStartObject();
-            writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, cap.getNamespace());
-            if ( !cap.getAttributes().isEmpty() ) {
-                generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
-                cap.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
-                generator.writeEnd();
-            }
-            if ( !cap.getDirectives().isEmpty() ) {
-                generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
-                cap.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
-                generator.writeEnd();
-            }
-            generator.writeEnd();
-        }
-
-        generator.writeEnd();
-    }
-
     private void writeFeature(final Writer writer, final Feature feature)
     throws IOException {
         JsonGenerator generator = newGenerator(writer);
diff --git a/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java b/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java
index 4602f2c..90b5668 100644
--- a/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java
+++ b/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java
@@ -20,6 +20,8 @@
 import org.apache.felix.configurator.impl.json.JSONUtil;
 import org.apache.felix.configurator.impl.json.TypeConverter;
 import org.apache.felix.configurator.impl.model.Config;
+import org.apache.felix.utils.resource.CapabilityImpl;
+import org.apache.felix.utils.resource.RequirementImpl;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Bundles;
@@ -28,7 +30,10 @@
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Extensions;
+import org.apache.sling.feature.Include;
 import org.apache.sling.feature.KeyValueMap;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 import java.io.IOException;
 import java.io.Reader;
@@ -44,6 +49,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.function.BiConsumer;
 import java.util.Set;
 
 import javax.json.Json;
@@ -489,4 +495,195 @@
             throw new IOException(this.exceptionPrefix + "Key " + key + " is not one of the allowed types " + Arrays.toString(types) + " : " + val.getClass());
         }
     }
+
+    protected Include readInclude(final Map<String, Object> map) throws IOException {
+        if ( map.containsKey(JSONConstants.FEATURE_INCLUDE)) {
+            final Object includeObj = map.get(JSONConstants.FEATURE_INCLUDE);
+            checkType(JSONConstants.FEATURE_INCLUDE, includeObj, Map.class, String.class);
+
+            final Include include;
+            if ( includeObj instanceof String ) {
+                final ArtifactId id = ArtifactId.parse(includeObj.toString());
+                include = new Include(id);
+            } else {
+                @SuppressWarnings("unchecked")
+                final Map<String, Object> obj = (Map<String, Object>) includeObj;
+                if ( !obj.containsKey(JSONConstants.ARTIFACT_ID) ) {
+                    throw new IOException(exceptionPrefix + " include is missing required artifact id");
+                }
+                checkType("Include " + JSONConstants.ARTIFACT_ID, obj.get(JSONConstants.ARTIFACT_ID), String.class);
+                final ArtifactId id = ArtifactId.parse(obj.get(JSONConstants.ARTIFACT_ID).toString());
+                include = new Include(id);
+
+                if ( obj.containsKey(JSONConstants.INCLUDE_REMOVALS) ) {
+                    checkType("Include removals", obj.get(JSONConstants.INCLUDE_REMOVALS), Map.class);
+                    @SuppressWarnings("unchecked")
+                    final Map<String, Object> removalObj = (Map<String, Object>) obj.get(JSONConstants.INCLUDE_REMOVALS);
+                    if ( removalObj.containsKey(JSONConstants.FEATURE_BUNDLES) ) {
+                        checkType("Include removal bundles", removalObj.get(JSONConstants.FEATURE_BUNDLES), List.class);
+                        @SuppressWarnings("unchecked")
+                        final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_BUNDLES);
+                        for(final Object val : list) {
+                            checkType("Include removal bundles", val, String.class);
+                            if ( val.toString().startsWith("#")) {
+                                continue;
+                            }
+                            include.getBundleRemovals().add(ArtifactId.parse(val.toString()));
+                        }
+                    }
+                    if ( removalObj.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
+                        checkType("Include removal configuration", removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS), List.class);
+                        @SuppressWarnings("unchecked")
+                        final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS);
+                        for(final Object val : list) {
+                            checkType("Include removal configuration", val, String.class);
+                            include.getConfigurationRemovals().add(val.toString());
+                        }
+                    }
+                    if ( removalObj.containsKey(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES) ) {
+                        checkType("Include removal framework properties", removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES), List.class);
+                        @SuppressWarnings("unchecked")
+                        final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
+                        for(final Object val : list) {
+                            checkType("Include removal framework properties", val, String.class);
+                            include.getFrameworkPropertiesRemovals().add(val.toString());
+                        }
+                    }
+                    if ( removalObj.containsKey(JSONConstants.INCLUDE_EXTENSION_REMOVALS) ) {
+                        checkType("Include removal extensions", removalObj.get(JSONConstants.INCLUDE_EXTENSION_REMOVALS), List.class);
+                        @SuppressWarnings("unchecked")
+                        final List<Object> list = (List<Object>)removalObj.get(JSONConstants.INCLUDE_EXTENSION_REMOVALS);
+                        for(final Object val : list) {
+                            checkType("Include removal extension", val, String.class, Map.class);
+                            if ( val instanceof String ) {
+                                if ( val.toString().startsWith("#")) {
+                                    continue;
+                                }
+                                include.getExtensionRemovals().add(val.toString());
+                            } else {
+                                @SuppressWarnings("unchecked")
+                                final Map<String, Object> removalMap = (Map<String, Object>)val;
+                                final Object nameObj = removalMap.get("name");
+                                checkType("Include removal extension", nameObj, String.class);
+                                if ( removalMap.containsKey("artifacts") ) {
+                                    checkType("Include removal extension artifacts", removalMap.get("artifacts"), List.class);
+                                    @SuppressWarnings("unchecked")
+                                    final List<Object> artifactList = (List<Object>)removalMap.get("artifacts");
+                                    final List<ArtifactId> ids = new ArrayList<>();
+                                    for(final Object aid : artifactList) {
+                                        checkType("Include removal extension artifact", aid, String.class);
+                                        ids.add(ArtifactId.parse(aid.toString()));
+                                    }
+                                    include.getArtifactExtensionRemovals().put(nameObj.toString(), ids);
+                                } else {
+                                    include.getExtensionRemovals().add(nameObj.toString());
+                                }
+                            }
+                        }
+                    }
+
+                }
+            }
+            return include;
+        }
+        return null;
+    }
+
+    protected void readRequirements(Map<String, Object> map, final List<Requirement> container) throws IOException {
+        if ( map.containsKey(JSONConstants.FEATURE_REQUIREMENTS)) {
+            final Object reqObj = map.get(JSONConstants.FEATURE_REQUIREMENTS);
+            checkType(JSONConstants.FEATURE_REQUIREMENTS, reqObj, List.class);
+
+            @SuppressWarnings("unchecked")
+            final List<Object> requirements = (List<Object>)reqObj;
+            for(final Object req : requirements) {
+                checkType("Requirement", req, Map.class);
+                @SuppressWarnings("unchecked")
+                final Map<String, Object> obj = (Map<String, Object>) req;
+
+                if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
+                    throw new IOException(this.exceptionPrefix + "Namespace is missing for requirement");
+                }
+                checkType("Requirement namespace", obj.get(JSONConstants.REQCAP_NAMESPACE), String.class);
+
+                Map<String, Object> attrMap = new HashMap<>();
+                if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
+                    checkType("Requirement attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
+                    @SuppressWarnings("unchecked")
+                    final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
+                    attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
+                }
+
+                Map<String, String> dirMap = new HashMap<>();
+                if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
+                    checkType("Requirement directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
+                    @SuppressWarnings("unchecked")
+                    final Map<String, Object> dirs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_DIRECTIVES);
+                    dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
+                }
+
+                final Requirement r = new RequirementImpl(null, obj.get(JSONConstants.REQCAP_NAMESPACE).toString(), dirMap, attrMap);
+                container.add(r);
+            }
+        }
+    }
+
+    protected void readCapabilities(Map<String, Object> map, final List<Capability> container) throws IOException {
+        if ( map.containsKey(JSONConstants.FEATURE_CAPABILITIES)) {
+            final Object capObj = map.get(JSONConstants.FEATURE_CAPABILITIES);
+            checkType(JSONConstants.FEATURE_CAPABILITIES, capObj, List.class);
+
+            @SuppressWarnings("unchecked")
+            final List<Object> capabilities = (List<Object>)capObj;
+            for(final Object cap : capabilities) {
+                checkType("Capability", cap, Map.class);
+                @SuppressWarnings("unchecked")
+                final Map<String, Object> obj = (Map<String, Object>) cap;
+
+                if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
+                    throw new IOException(this.exceptionPrefix + "Namespace is missing for capability");
+                }
+                checkType("Capability namespace", obj.get(JSONConstants.REQCAP_NAMESPACE), String.class);
+
+                Map<String, Object> attrMap = new HashMap<>();
+                if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
+                    checkType("Capability attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
+                    @SuppressWarnings("unchecked")
+                    final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
+                    attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
+                }
+
+                Map<String, String> dirMap = new HashMap<>();
+                if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
+                    checkType("Capability directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
+                    @SuppressWarnings("unchecked")
+                    final Map<String, Object> dirs = (Map<String, Object>) obj.get(JSONConstants.REQCAP_DIRECTIVES);
+                    dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
+                }
+
+                final Capability c = new CapabilityImpl(null, obj.get(JSONConstants.REQCAP_NAMESPACE).toString(), dirMap, attrMap);
+                container.add(c);
+            }
+        }
+    }
+
+    @FunctionalInterface
+    private interface BiConsumer_WithExceptions<T, V, E extends Exception> {
+        void accept(T t, V u) throws E;
+    }
+
+    private static <T, V, E extends Exception> BiConsumer<T, V> rethrowBiConsumer(BiConsumer_WithExceptions<T, V, E> biConsumer) {
+        return (t, u) -> {
+            try {
+                biConsumer.accept(t, u);
+            } catch (Exception exception) {
+                throwAsUnchecked(exception);
+            }
+        };
+    }
+
+    @SuppressWarnings ("unchecked")
+    private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
+        throw (E) exception;
+    }
 }
diff --git a/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java b/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
index 3533e68..23fb2df 100644
--- a/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
+++ b/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
@@ -19,8 +19,8 @@
 import java.io.StringReader;
 import java.io.Writer;
 import java.lang.reflect.Array;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
 
@@ -30,12 +30,16 @@
 import javax.json.stream.JsonGeneratorFactory;
 
 import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Bundles;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Configurations;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
+import org.apache.sling.feature.Include;
 import org.apache.sling.feature.KeyValueMap;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
 
 /**
  * Common functionality for writing JSON
@@ -109,14 +113,13 @@
 
             generator.writeStartObject(key);
 
-            final Enumeration<String> e = cfg.getProperties().keys();
-            while ( e.hasMoreElements() ) {
-                final String name = e.nextElement();
+            for(final Map.Entry<String, Object> prop : cfg.getProperties().entrySet()) {
+                final String name = prop.getKey();
                 if ( Configuration.PROP_ARTIFACT_ID.equals(name) ) {
                     continue;
                 }
 
-                final Object val = cfg.getProperties().get(name);
+                final Object val = prop.getValue();
 
                 String typePostFix = null;
                 final Object typeCheck;
@@ -256,4 +259,114 @@
             }
         }
     }
+
+    protected void writeProperty(final JsonGenerator generator, final String key, final String value) {
+        if ( value != null ) {
+            generator.write(key, value);
+        }
+    }
+
+    protected <T> void writeList(final JsonGenerator generator, final String name, final Collection<T> values) {
+        if (!values.isEmpty()) {
+            generator.writeStartArray(name);
+            for (T value : values) {
+                generator.write(value.toString());
+            }
+            generator.writeEnd();
+        }
+    }
+
+    protected void writeInclude(final JsonGenerator generator, final Include inc) {
+        if (inc == null) {
+            return;
+        }
+
+        if ( inc.getArtifactExtensionRemovals().isEmpty()
+             && inc.getBundleRemovals().isEmpty()
+             && inc.getConfigurationRemovals().isEmpty()
+             && inc.getFrameworkPropertiesRemovals().isEmpty() ) {
+
+            generator.write(JSONConstants.FEATURE_INCLUDE, inc.getId().toMvnId());
+        } else {
+            generator.writeStartObject(JSONConstants.FEATURE_INCLUDE);
+            writeProperty(generator, JSONConstants.ARTIFACT_ID, inc.getId().toMvnId());
+
+            generator.writeStartObject(JSONConstants.INCLUDE_REMOVALS);
+
+            if ( !inc.getArtifactExtensionRemovals().isEmpty()
+                 || inc.getExtensionRemovals().isEmpty() ) {
+                generator.writeStartArray(JSONConstants.INCLUDE_EXTENSION_REMOVALS);
+
+                for(final String id : inc.getExtensionRemovals()) {
+                    generator.write(id);
+                }
+                for(final Map.Entry<String, List<ArtifactId>> entry : inc.getArtifactExtensionRemovals().entrySet()) {
+                    generator.writeStartObject();
+
+                    writeList(generator, entry.getKey(), entry.getValue());
+
+                    generator.writeEnd();
+                }
+
+                generator.writeEnd();
+            }
+            writeList(generator, JSONConstants.FEATURE_CONFIGURATIONS, inc.getConfigurationRemovals());
+            writeList(generator, JSONConstants.FEATURE_BUNDLES, inc.getBundleRemovals());
+            writeList(generator, JSONConstants.FEATURE_FRAMEWORK_PROPERTIES, inc.getFrameworkPropertiesRemovals());
+
+            generator.writeEnd().writeEnd();
+        }
+    }
+
+    protected void writeRequirements(final JsonGenerator generator, final List<Requirement> requirements) {
+        if (requirements.isEmpty()) {
+            return;
+        }
+
+        generator.writeStartArray(JSONConstants.FEATURE_REQUIREMENTS);
+
+        for(final Requirement req : requirements) {
+            generator.writeStartObject();
+            writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, req.getNamespace());
+            if ( !req.getAttributes().isEmpty() ) {
+                generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
+                req.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
+                generator.writeEnd();
+            }
+            if ( !req.getDirectives().isEmpty() ) {
+                generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
+                req.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
+                generator.writeEnd();
+            }
+            generator.writeEnd();
+        }
+
+        generator.writeEnd();
+    }
+
+    protected void writeCapabilities(final JsonGenerator generator, final List<Capability> capabilities) {
+        if (capabilities.isEmpty()) {
+            return;
+        }
+
+        generator.writeStartArray(JSONConstants.FEATURE_CAPABILITIES);
+
+        for(final Capability cap : capabilities) {
+            generator.writeStartObject();
+            writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, cap.getNamespace());
+            if ( !cap.getAttributes().isEmpty() ) {
+                generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
+                cap.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
+                generator.writeEnd();
+            }
+            if ( !cap.getDirectives().isEmpty() ) {
+                generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
+                cap.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
+                generator.writeEnd();
+            }
+            generator.writeEnd();
+        }
+
+        generator.writeEnd();
+    }
 }