/** * Copyright 2015 ArcBees Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * 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. */ package com.arcbees.gaestudio.server.util; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.arcbees.gaestudio.shared.PropertyType; import static com.arcbees.gaestudio.shared.PropertyName.GAE_PROPERTY_TYPE; import static com.arcbees.gaestudio.shared.PropertyName.ID; import static com.arcbees.gaestudio.shared.PropertyName.KEY; import static com.arcbees.gaestudio.shared.PropertyName.KIND; import static com.arcbees.gaestudio.shared.PropertyName.LATITUDE; import static com.arcbees.gaestudio.shared.PropertyName.LONGITUDE; import static com.arcbees.gaestudio.shared.PropertyName.PROPERTY_MAP; import static com.arcbees.gaestudio.shared.PropertyName.VALUE; public class JsonToCsvConverter { public String convert(String jsonData) throws JSONException { Set<String> columns; JSONArray array = new JSONArray(jsonData); JSONObject fullPropertyMap = extractFullPropertyMap(array); columns = extractColumns(fullPropertyMap); String csvResult = buildColumnsLine(columns); csvResult += buildDataLines(array, fullPropertyMap); return csvResult; } private JSONObject extractFullPropertyMap(JSONArray array) { JSONObject currentObjectPropertyMap; JSONObject fullPropertyMap = new JSONObject(); for (int i = 0; i < array.length(); i++) { currentObjectPropertyMap = array.getJSONObject(i).getJSONObject(PROPERTY_MAP); updateFullPropertyMap(currentObjectPropertyMap, fullPropertyMap); } return fullPropertyMap; } private void updateFullPropertyMap(JSONObject currentObjectPropertyMap, JSONObject fullPropertyMap) { Iterator currentObjectKeys = currentObjectPropertyMap.keys(); while (currentObjectKeys.hasNext()) { String propertyName = String.valueOf(currentObjectKeys.next()); JSONObject currentObject = currentObjectPropertyMap.getJSONObject(propertyName); if (!fullPropertyMap.has(propertyName)) { fullPropertyMap.accumulate(propertyName, currentObject); } } } private Set<String> extractColumns(JSONObject fullPropertyMap) throws JSONException { Set<String> columns = new LinkedHashSet<>(); Iterator currentObjectKeys = fullPropertyMap.keys(); while (currentObjectKeys.hasNext()) { columns.addAll( generateColumnNamesFromPropertyMap(String.valueOf(currentObjectKeys.next()), fullPropertyMap)); } return columns; } private String buildColumnsLine(Set<String> columns) { String columnsResult = ID + ", "; int counter = 1; for (String column : columns) { columnsResult += column; columnsResult = addSeparator(columns, columnsResult, counter); counter++; } return columnsResult; } private String addSeparator(Set<String> columns, String columnsResult, int counter) { String result = columnsResult; if (counter < columns.size()) { result += ", "; } else { result += "\n"; } return result; } private String buildDataLines(JSONArray dataArray, JSONObject fullPropertyMap) throws JSONException { JSONObject propertyMap; JSONObject currentObject; String dataLines = ""; String currentId; for (int i = 0; i < dataArray.length(); i++) { currentObject = dataArray.getJSONObject(i); currentId = currentObject.getJSONObject(KEY).get(ID).toString(); propertyMap = currentObject.getJSONObject(PROPERTY_MAP); dataLines += currentId; dataLines += generateDataLineFromPropertyMap(propertyMap, fullPropertyMap); } return dataLines; } private String writeKeyData(JSONObject currentObjectValue) throws JSONException { String currentKind = currentObjectValue.getString(KIND); String currentId = currentObjectValue.get(ID).toString(); return currentKind + "(" + currentId + ")"; } private Set<String> generateColumnNamesFromPropertyMap(String propertyName, JSONObject propertyMap) { JSONObject currentPropertyObject = propertyMap.getJSONObject(propertyName); return generateColumnNamesFromPropertyType(propertyName, currentPropertyObject); } private Set<String> generateColumnNamesFromPropertyType(String propertyName, JSONObject currentPropertyObject) { Set<String> columns = new LinkedHashSet<>(); if (currentPropertyObject.has(GAE_PROPERTY_TYPE)) { columns.addAll(generateColumns(propertyName, currentPropertyObject)); } else { columns.add(propertyName); } return columns; } private Set<String> generateColumns(String propertyName, JSONObject currentPropertyObject) { Set<String> columns = new LinkedHashSet<>(); PropertyType propertyType = PropertyType.valueOf(currentPropertyObject.getString(GAE_PROPERTY_TYPE)); switch (propertyType) { case STRING: case KEY: case NUMERIC: case BOOLEAN: case FLOATING: columns.add(propertyName); break; case GEO_PT: columns.addAll(generateGeoPtColumnNames(propertyName)); break; case COLLECTION: columns.addAll(generateArrayColumnNames(propertyName, currentPropertyObject.getJSONArray(VALUE))); break; } return columns; } private Set<String> generateArrayColumnNames(String propertyName, JSONArray array) { Set<String> arrayColumns = new LinkedHashSet<>(); for (int i = 0; i < array.length(); i++) { arrayColumns.addAll( generateColumnNamesFromPropertyType(propertyName + "[" + i + "]", array.getJSONObject(i))); } return arrayColumns; } private Set<String> generateGeoPtColumnNames(String propertyName) { Set<String> geoColumns = new LinkedHashSet<>(); geoColumns.add(propertyName + "." + LATITUDE); geoColumns.add(propertyName + "." + LONGITUDE); return geoColumns; } private String generateDataLineFromPropertyMap(JSONObject propertyMap, JSONObject fullPropertyMap) { Iterator fullPropertyMapKeys = fullPropertyMap.keys(); String dataLine = ""; while (fullPropertyMapKeys.hasNext()) { dataLine += ", "; String propertyName = String.valueOf(fullPropertyMapKeys.next()); dataLine += writePropertyData(propertyName, propertyMap); } return dataLine + "\n"; } private String writePropertyData(String propertyName, JSONObject propertyMap) { String propertyData = ""; if (propertyMap.has(propertyName)) { JSONObject currentPropertyObject = propertyMap.getJSONObject(propertyName); propertyData += writePropertyData(currentPropertyObject); } return propertyData; } private String writeGeoData(JSONObject geoPt) { return geoPt.get(LATITUDE) + ", " + geoPt.get(LONGITUDE); } private String writeArrayData(JSONArray array) { String arrayData = ""; for (int i = 0; i < array.length(); i++) { arrayData += writePropertyData(array.getJSONObject(i)); arrayData += ", "; } return removeLastComma(arrayData); } private String removeLastComma(String arrayData) { return new StringBuilder(arrayData).delete(arrayData.length() - 2, arrayData.length()).toString(); } private String writePropertyData(JSONObject jsonObject) { String propertyData = ""; if (jsonObject.has(GAE_PROPERTY_TYPE)) { propertyData += writeData(jsonObject); } else { propertyData += String.valueOf(jsonObject.get(VALUE)); } return propertyData; } private String writeData(JSONObject jsonObject) { PropertyType propertyType = PropertyType.valueOf(jsonObject.getString(GAE_PROPERTY_TYPE)); switch (propertyType) { case STRING: case NUMERIC: case BOOLEAN: case FLOATING: return String.valueOf(jsonObject.get(VALUE)); case GEO_PT: return writeGeoData((JSONObject) jsonObject.get(VALUE)); case COLLECTION: return writeArrayData((JSONArray) jsonObject.get(VALUE)); case KEY: return writeKeyData((JSONObject) jsonObject.get(VALUE)); } return String.valueOf(jsonObject.get(VALUE)); } }