class JsonResource extends ResourceFilter {

    @Override
    public void write(OutputStream outStream, LanguageBundle languageBundle,
            FilterOptions options) throws IOException, ResourceFilterException {
        // extracts key value pairs in original sequence order
        List<ResourceString> resStrings = languageBundle.getSortedResourceStrings();

        JsonObject output = new JsonObject();
        JsonObject top_level;

        if (this instanceof GlobalizeJsResource) {
            String resLanguageCode = languageBundle.getEmbeddedLanguageCode();
            if (resLanguageCode == null || resLanguageCode.isEmpty()) {
                throw new ResourceFilterException("Missing resource language code in the specified language bundle.");
            }
            top_level = new JsonObject();
            top_level.add(resLanguageCode, output);
        } else {
            top_level = output;
        }

        for (ResourceString res : resStrings) {
            String key = res.getKey();
            List<KeyPiece> keyPieces = splitKeyPieces(key);
            JsonElement current = output;
            for (int i = 0; i < keyPieces.size(); i++) {
                if (i + 1 < keyPieces.size()) { // There is structure under this
                                                // key piece
                    if (current.isJsonObject()) {
                        JsonObject currentObject = current.getAsJsonObject();
                        if (!currentObject.has(keyPieces.get(i).keyValue)) {
                            if (keyPieces.get(i + 1).keyType == JsonToken.BEGIN_ARRAY) {
                                currentObject.add(keyPieces.get(i).keyValue, new JsonArray());
                            } else {
                                currentObject.add(keyPieces.get(i).keyValue, new JsonObject());
                            }
                        }
                        current = currentObject.get(keyPieces.get(i).keyValue);
                    } else {
                        JsonArray currentArray = current.getAsJsonArray();
                        Integer idx = Integer.valueOf(keyPieces.get(i).keyValue);
                        for (int arrayIndex = currentArray.size(); arrayIndex <= idx; arrayIndex++) {
                            currentArray.add(JsonNull.INSTANCE);
                        }
                        if (currentArray.get(idx).isJsonNull()) {
                            if (keyPieces.get(i + 1).keyType == JsonToken.BEGIN_ARRAY) {
                                currentArray.set(idx, new JsonArray());
                            } else {
                                currentArray.set(idx, new JsonObject());
                            }
                        }
                        current = currentArray.get(idx);
                    }
                } else { // This is the leaf node
                    if (keyPieces.get(i).keyType == JsonToken.BEGIN_ARRAY) {
                        JsonArray currentArray = current.getAsJsonArray();
                        Integer idx = Integer.valueOf(keyPieces.get(i).keyValue);
                        JsonPrimitive e = new JsonPrimitive(res.getValue());
                        for (int arrayIndex = currentArray.size(); arrayIndex <= idx; arrayIndex++) {
                            currentArray.add(JsonNull.INSTANCE);
                        }
                        current.getAsJsonArray().set(idx, e);
                    } else {
                        current.getAsJsonObject().addProperty(keyPieces.get(i).keyValue, res.getValue());
                    }
                }
            }
        }
        try (OutputStreamWriter writer = new OutputStreamWriter(new BufferedOutputStream(outStream),
                StandardCharsets.UTF_8)) {
            new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(top_level, writer);
        }
    }

}