package org.gcube.portlets.user.geoportaldataentry.server; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import org.gcube.application.geoportalcommon.shared.geoportal.config.GcubeProfileDV; import org.gcube.portlets.user.geoportaldataentry.shared.GeoNaFormDataObject; import org.gcube.portlets.widgets.mpformbuilder.shared.GenericDatasetBean; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The Class FormDataObjectToJSON. * * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it * * Mar 10, 2022 */ public class FormDataObjectToJSON { private static final Logger LOG = LoggerFactory.getLogger(FormDataObjectToJSON.class); /** * Convert. * * @param listGeonaFormObjects the list geona form objects * @return the JSON object * @throws JSONException the JSON exception */ public JSONObject convert(List listGeonaFormObjects) throws JSONException { JSONObject theDocument = JSONObjecOrdered.instance(); for (GeoNaFormDataObject geoNaFormDataObject : listGeonaFormObjects) { List listGDB = geoNaFormDataObject.getListGDB(); GcubeProfileDV profile = geoNaFormDataObject.getGcubeProfileDV(); LOG.debug("The profile is: " + profile); String jsonPathExp = String.format("%s%s", profile.getParentName(), profile.getSectionName()); LOG.debug("The json path to build: " + jsonPathExp); // jsonPathExp = "$.chidl1.child2.child3"; String toJsonPathExp = jsonPathExp.replaceFirst("\\$", ""); String[] jsonPathDeep = toJsonPathExp.split("\\."); List listJSONObject = toListJonObject(listGDB, jsonPathDeep); // adding as JSONObject to theDocument at first deep level (under the root or at section specified) if (listJSONObject.size() == 1) { theDocument = deepMerge(listJSONObject.get(0), theDocument); //theDocument = theDocument.put(listJSONObject.get(0), jsonArray); } else { // adding as JSONArray to theDocument under the section specified // to jsonPathDeep[1] JSONArray jsonArray = new JSONArray(); for (JSONObject jsonObject : listJSONObject) { // reading the i-mo JSONObject created in the list with the key as // jsonPathDeep[1] jsonArray.put(jsonObject.get(jsonPathDeep[1])); } theDocument.put(jsonPathDeep[1], jsonArray); } LOG.debug("Partial Root Document is: " + theDocument); } LOG.debug("Final JSON Document is: " + theDocument); return theDocument; } /** * Generic dataset bean to JSON. * * @param gdb the gdb * @return the JSON object * @throws JSONException the JSON exception */ private JSONObject genericDatasetBeanToJSON(GenericDatasetBean gdb) throws JSONException { JSONObject sectJSONObject = JSONObjecOrdered.instance(); LinkedHashMap> mapFields = gdb.getFormDataEntryFields(); LOG.debug("Map ordered: " + mapFields); for (String key : mapFields.keySet()) { List listValues = mapFields.get(key); if (listValues == null || listValues.isEmpty()) { continue; } // key/value as string if (listValues.size() == 1) { sectJSONObject.put(key, listValues.get(0)); continue; } // value is a list JSONArray array = new JSONArray(); for (String value : listValues) { array.put(value); } sectJSONObject.put(key, array); } return sectJSONObject; } /** * To list jon object. * * @param listGDB the list GDB * @param jsonPathDeep the json path deep * @return the list * @throws JSONException the JSON exception */ private List toListJonObject(List listGDB, String[] jsonPathDeep) throws JSONException { List listJSONObject = new ArrayList(); for (GenericDatasetBean gdb : listGDB) { JSONObject sectRootObject = JSONObjecOrdered.instance(); JSONObject jsonObject = genericDatasetBeanToJSON(gdb); LOG.debug("Adding section : " + jsonObject); LOG.trace("jsonPathDeep: " + Arrays.asList(jsonPathDeep) + " size: " + jsonPathDeep.length); // Adding JSONObject to ROOT DOCUMENT POSITION (using PLACEHOLDER_ROOT_POINTER_JSON_DOC) if (jsonPathDeep.length == 0) { sectRootObject = deepMerge(jsonObject, sectRootObject); } else { // DEPTH >= 1 sectRootObject.put(jsonPathDeep[jsonPathDeep.length - 1], jsonObject); JSONObject deepJSON = sectRootObject; System.out.println("sectRootObject: " + sectRootObject.toString()); for (int i = jsonPathDeep.length - 2; i > 0; i--) { JSONObject newOne = JSONObjecOrdered.instance(); newOne.put(jsonPathDeep[i], deepJSON); deepJSON = newOne; } sectRootObject = deepJSON; } listJSONObject.add(sectRootObject); } LOG.info("For listGDB : " + listGDB); LOG.info("returning : " + listJSONObject); return listJSONObject; } /** * Merge "source" into "target". If fields have equal name, merge them * recursively. * * @param source the source * @param target the target * @return the merged object (target). * @throws JSONException the JSON exception */ public static JSONObject deepMerge(JSONObject source, JSONObject target) throws JSONException { for (String key : JSONObject.getNames(source)) { Object value = source.get(key); if (!target.has(key)) { // new value for "key": target.put(key, value); } else { // existing value for "key" - recursively deep merge: if (value instanceof JSONObject) { JSONObject valueJson = (JSONObject) value; deepMerge(valueJson, target.getJSONObject(key)); } else { target.put(key, value); } } } return target; } /** * The Class JSONObjecOrdered. * * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it * * Mar 10, 2022 */ public static class JSONObjecOrdered { /** * Instance. * * @return the JSON object */ public static JSONObject instance() { JSONObject jsonObject = new JSONObject(); try { Field changeMap = jsonObject.getClass().getDeclaredField("map"); changeMap.setAccessible(true); changeMap.set(jsonObject, new LinkedHashMap<>()); changeMap.setAccessible(false); } catch (IllegalAccessException | NoSuchFieldException e) { } return jsonObject; } } }