Did you know? Oracle Mobile: JSONBeanSerializationHelper Does Not Respect Case!

Did you know?  Oracle Mobile:  JSONBeanSerializationHelper Does Not Respect Case!

shutterstock_273386390Oracle Mobile is a framework provided by Oracle that enables developers to build cross-platform mobile applications.  At AST, a passionate set of developers are using Oracle Mobile to build mobile applications that will be used as an extension for Oracle Cloud Products.  During our development life-cycle, we discovered a fundamental issue with the Oracle Mobile framework, particularly with the JSONBeanSerializationHelper class.

The role of the JSONBeanSerializationHelper class within the Oracle Mobile framework is to convert JAVA objects to JSON strings.  A JSON string is an industry standard accepted for data representation; it consists of name: value pairs.  According to the JSON Specification, a JSON String is always case-sensitive.  When the JSONBeanSerializationHelper converts a JAVA object into name: value pairs, it doesn’t respect the variable case in the JAVA object.  All variables are converted to names in JSON strings in lowercase.

We took this up with Oracle and have an Enhancement Request (ER) logged.  We will update this post once the ER has been resolved.

Described below is a use case and how this impacts the mobile application integration with Oracle Service Cloud.  However, you could face the same issue when integrating with other Oracle Cloud Products:

Use Case

Consider a use case in which you have a mobile application built using Oracle Mobile.  The application has a feature that allows you to create an incident in Oracle Service Cloud.

Issue Details

  • Consider an Incident bean defined as:
public class Incident {
    private Integer id = null;
    private String lookupName = null;
    private String createdTime = null;
    private String updatedTime = null;
    private Asset asset = null;
    private Contact primaryContact = null;
    private String subject = null;
    private Threads threads = null;
    private AssignedTo assignedTo = null;
    private Banner banner = null;
    private String category = null;
    private String channel = null;
    private String chatQueue = null;
    private Date closedTime = null;
    private Account createdByAccount = null;
    private ParentInc ParentInc = null;
    private CustomFields customFields = null;
    private Link[] links;
    private PropertyChangeSupport _propertyChangeSupport = new PropertyChangeSupport(this);
    ..
    ..
    ..
}
  • An incident object will be instantiated and initialized when the user inputs the required values from the mobile interface.
  • The incident object is then converted to JSON using oracle.adfmf.framework.api.JSONBeanSerializationHelper
  public static Incident convertFromJSON(String response) {
        try {
            incident = (Incident) JSONBeanSerializationHelper.fromJSON(Incident.class, response);
        } catch (Exception e) {
            Debug.print(e.getLocalizedMessage());
        }
        return incident;
    }
  • Converted jsonObject:
{  
   "updatedTime":{  
      ".null":true
   },
   "createdByAccount":{  
      ".null":true
   },
   "subject":"Curb damaged",
   "customFields":{  
      "c":{  
         "incident_address":"200 East Santa Clara Street San Jose California 95113",
         "incident_zip_postal_code":"95113",
         "incident_state_province":"California",
         "incident_latitude":"37.3378484",
         "incident_city":"San Jose",
         ".type":"com.astcorporation.CRMServiceMobile.api.entities.C",
         "incident_longitude":"-121.8861262",
         "incident_street_address":"200 East Santa Clara Street"
      },
      "transit":{  
         ".type":"com.astcorporation.CRMServiceMobile.api.entities.Transit",
         "latitude":"37.3378484",
         "longitude":"-121.8861262",
         "incidentMappingAddress":"200 East Santa Clara Street San Jose Califo"
      },
      ".type":"com.astcorporation.CRMServiceMobile.api.entities.CustomFields",
      "propertyChangeSupport":{  
         ".type":"oracle.adfmf.java.beans.PropertyChangeSupport",
         "propertyChangeListeners":[  

         ]
      },
      "store":{  
         ".null":true
      }
   },
   "channel":{  
      ".null":true
   },
   "threads":{  
      "entryType":{  
         ".type":"com.astcorporation.CRMServiceMobile.api.entities.Entry",
         "id":3
      },
      ".type":"com.astcorporation.CRMServiceMobile.api.entities.Threads",
      "text":"Curb on Ogden & River Road intersection is damaged"
   },
   "banner":{  
      ".null":true
   },
   "propertyChangeSupport":{  
      ".type":"oracle.adfmf.java.beans.PropertyChangeSupport",
      "propertyChangeListeners":[  

      ]
   },
   "assignedTo":{  
      ".null":true
   },
   "closedTime":{  
      ".null":true
   },
   "parentInc":{  
      ".null":true
   },
   "chatQueue":{  
      ".null":true
   },
   ".type":"com.astcorporation.CRMServiceMobile.api.entities.Incident",
   "primaryContact":{  
      ".type":"com.astcorporation.CRMServiceMobile.api.entities.Contact",
      "propertyChangeSupport":{  
         ".type":"oracle.adfmf.java.beans.PropertyChangeSupport",
         "propertyChangeListeners":[  

         ]
      },
      "id":1847
   },
   "createdTime":{  
      ".null":true
   },
   "links":{  
      ".null":true
   },
   "id":{  
      ".null":true
   },
   "category":{  
      ".null":true
   },
   "asset":{  
      ".null":true
   },
   "lookupName":{  
      ".null":true
   }
}
  • The JSON generated will not be accepted by the create incident API because:
    1. It contains “null” names: value pairs for variables that did not hold any values
    2. It contains a “type” name: value pair for variable with custom data types
    3. It contains “propertyChangeSupport” names: value pairs as the beans had a property change attribute defined. This is not expected by the create incident REST-API
    4. The JSON did not respect the case of the JAVA variables defined in the Incident bean when generating corresponding name: value pairs.
      1. For example: Consider the attribute “transit” in the JSON – the JAVA variable was defined as “Transit”. The create incident REST-API also expects the name to be “Transit”.
public class C {
    private String incident_latitude;
    private String incident_longitude;
    private String incident_street_address;
    private PropertyChangeSupport _propertyChangeSupport = new PropertyChangeSupport(this);
    ..
    ..
}

Workaround

  • Option 1: Create your own implementation of the JSON serialization class. 
  • Option 2: Create a method to format your JSON as a break-fix.
    • Call custom method removeNullsAndTypeFromJSON post-JSON conversion
public static JSONObject convertToJSON(Incident incident) {
        try {
            jsonObject = (JSONObject) JSONBeanSerializationHelper.toJSON(incident);
            removeNullsAndTypeFromJSON(jsonObject);

        } catch (Exception e) {
            Debug.print(e.getLocalizedMessage());
        }
        return jsonObject;
    }
  • removeNullsAndTypeFromJSON” method implementation:
protected static void removeNullsAndTypeFromJSON(JSONObject jsonObj) throws JSONException {
        jsonObj.remove(".type");
        jsonObj.remove("propertyChangeSupport");

        for (int i = 0; i < jsonObj.length(); i++) {
            String key = jsonObj.names().getString(i);
            if (JSONBeanSerializationHelper.isObjectNull(jsonObj.get(key))) {
                jsonObj.remove(key);
                i--;
            } else {
                if (jsonObj.get(key).toString().contains(".type")) {
                    removeNullsAndTypeFromJSON(jsonObj.getJSONObject(key));
                }
                if (jsonObj.get(key).toString().contains(".null")) {
                    removeNullsAndTypeFromJSON(jsonObj.getJSONObject(key));
                }
            }
            //BUG: JSONBeanSerializationHelper does not variable match case
            if ("transit".equals(key)) {
                JSONObject transit = jsonObj.getJSONObject("transit");
                jsonObj.remove("transit");
                String Latitude = transit.getString("latitude");
                transit.remove("latitude");
                transit.put("Latitude", Latitude);
                String Longitude = transit.getString("longitude");
                transit.remove("longitude");
                transit.put("Longitude", Longitude);
                String IncidentMappingAddress = transit.getString("incidentMappingAddress");
                transit.remove("incidentMappingAddress");
                transit.put("IncidentMappingAddress", IncidentMappingAddress);
                jsonObj.put("Transit", transit);
            }
        }
    }

Reference

Tagged with: , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

*