Package org.opensocial.client

Source Code of org.opensocial.client.OpenSocialJsonParser

/* Copyright (c) 2009 Google 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 org.opensocial.client;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.opensocial.data.OpenSocialActivity;
import org.opensocial.data.OpenSocialAppData;
import org.opensocial.data.OpenSocialField;
import org.opensocial.data.OpenSocialObject;
import org.opensocial.data.OpenSocialPerson;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
* An object which exposes a number of static methods for parsing JSON strings
* returned from RESTful or JSON-RPC requests into appropriate objects.
*
* @author apijason@google.com (Jason Cooper)
*/
class OpenSocialJsonParser {

  /**
   * Parses the passed JSON string into an OpenSocialResponse object -- if the
   * passed string represents a JSON array, each object in the array is added
   * to the returned object keyed on its "id" property.
   *
   * @param  in The complete JSON string returned from an OpenSocial container
   *         in response to a request for data
   * @return
   */
  public static OpenSocialResponse getResponse(String in) throws
      OpenSocialRequestException {
    if (!isJsonArray(in)) {
      return null;
    }

    JSONArray responseArray = null;

    try {
      responseArray = new JSONArray(in);
    } catch (JSONException e) {
      throw new OpenSocialRequestException(
          "Invalid JSON array string " + in);
    }

    OpenSocialResponse r = new OpenSocialResponse();

    for (int i = 0; i < responseArray.length(); i++) {
      try {
        String id = null;
        JSONObject o = responseArray.getJSONObject(i);

        if (o.has("id")) {
          id = o.getString("id");

          try {
            JSONObject errorObject = o.getJSONObject("error");
            String errorMessage = errorObject.getString("message");
            String errorCode = errorObject.getString("code");

            if (errorCode != null && errorMessage != null) {
              throw new OpenSocialRequestException(
                  "Container returned error " + errorCode + " \"" +
                  errorMessage + "\" for request ID \"" + id + "\"");
            } else {
              throw new OpenSocialRequestException(
                  "Container returned error response " + o.toString());
            }
          } catch (JSONException e) {
            // Ignore exception thrown if error object or code and/or message
            // keys aren't found
          }

          r.addItem(id, escape(o.toString()));
        }
      } catch (JSONException e) {
        // Ignore exception thrown if object or ID string can't be parsed
        continue;
      }
    }

    return r;
  }

  /**
   * Parses the passed JSON string into an OpenSocialResponse object -- if the
   * passed string represents a JSON object, it is added to the returned
   * object keyed on the passed ID.
   *
   * @param in The complete JSON string returned from an OpenSocial container
   *        in response to a request for data
   * @param id The string ID to tag the JSON object string with as it is added
   *        to the OpenSocialResponse object
   * @return
   */
  public static OpenSocialResponse getResponse(String in, String id) throws
      OpenSocialRequestException {
    OpenSocialResponse r = null;

    if (isJsonObject(in)) {
      r = new OpenSocialResponse();
      r.addItem(id, escape(in));
    } else if (isJsonArray(in)) {
      return getResponse(in);
    }

    return r;
  }

  /**
   * Transforms a raw JSON object string containing profile information for a
   * single user into an OpenSocialPerson instance with all profile details
   * abstracted as OpenSocialField objects associated with the instance.
   *
   * @param in The JSON object string to parse as an OpenSocialPerson object
   * @return
   * @throws OpenSocialRequestException
   */
  public static OpenSocialPerson parseAsPerson(String in) throws
      OpenSocialRequestException {
    if (in == null) {
      throw new OpenSocialRequestException(
          "Response item with given key not found");
    }

    JSONObject root = null;
    try {
      root = new JSONObject(in);
    } catch (JSONException e) {
      throw new OpenSocialRequestException(
          "Invalid JSON object string " + in);
    }

    JSONObject entry = getEntryObject(root);

    return (OpenSocialPerson) parseAsObject(entry, OpenSocialPerson.class);
  }

  /**
   * Transforms a raw JSON object string containing profile information for a
   * group of users into a list of OpenSocialPerson instances with all profile
   * details abstracted as OpenSocialField objects associated with the
   * instances. These instances are then added to a Java List which
   * gets returned.
   *
   * @param in The JSON object string to parse as a List of OpenSocialPerson
   *        objects
   * @return
   * @throws OpenSocialRequestException
   */
  public static List<OpenSocialPerson> parseAsPersonCollection(String in)
      throws OpenSocialRequestException {
    if (in == null) {
      throw new OpenSocialRequestException(
          "Response item with given key not found");
    }

    JSONObject root = null;
    try {
      root = new JSONObject(in);
    } catch (JSONException e) {
      throw new OpenSocialRequestException(
          "Invalid JSON object string " + in);
    }

    JSONArray entries = getEntryArray(root);
    List<OpenSocialPerson> l =
      new ArrayList<OpenSocialPerson>(entries.length());

    for (int i = 0; i < entries.length(); i++) {
      try {
        JSONObject entry = entries.getJSONObject(i);
        l.add((OpenSocialPerson) parseAsObject(entry, OpenSocialPerson.class));
      } catch (JSONException e) {
        // Ignore exception thrown if object can't be parsed
        continue;
      }
    }

    return l;
  }

  /**
   * Transforms a raw JSON object string containing key-value pairs (i.e. App
   * Data) for one or more users into a specialized OpenSocialObject instance
   * with each key-value pair abstracted as OpenSocialField objects associated
   * with the instance.
   *
   * @param  in The JSON object string to parse as an OpenSocialAppData object
   * @return
   * @throws OpenSocialRequestException
   */
  public static OpenSocialAppData parseAsAppData(String in) throws
      OpenSocialRequestException {

    if (in == null) {
      throw new OpenSocialRequestException(
          "Response item with given key not found");
    }

    JSONObject root = null;
    try {
      root = new JSONObject(in);
    } catch (JSONException e) {
      throw new OpenSocialRequestException(
          "Invalid JSON object string " + in);
    }

    JSONObject entry = getEntryObject(root);

    return (OpenSocialAppData) parseAsObject(entry, OpenSocialAppData.class);
  }
 
  /**
   * Parses the input JSON string, builds OpenSocialActivity objects with the
   * the entry values and returns these in a List.
   *
   * @param in
   * @return
   * @throws OpenSocialRequestException
   */
  public static List<OpenSocialActivity> parseAsActivityCollection(String in)
    throws OpenSocialRequestException {
    if (in == null) {
      throw new OpenSocialRequestException(
        "Response item with given key not found");
    }

    JSONObject root = null;
    try {
      root = new JSONObject(in);
    } catch (JSONException e) {
      throw new OpenSocialRequestException(
          "Invalid JSON object string " + in);
    }

    JSONArray entries = getEntryArray(root);
    List<OpenSocialActivity> l =
      new ArrayList<OpenSocialActivity>(entries.length());

    for (int i = 0; i < entries.length(); i++) {
      try {
        JSONObject entry = entries.getJSONObject(i);
        l.add((OpenSocialActivity) parseAsObject(entry,
            OpenSocialActivity.class));
      } catch (JSONException e) {
        // Ignore exception thrown if object can't be parsed
        continue;
      }
    }

    return l;
  }

  /**
   * Inspects the passed object for one of several specific properties and, if
   * found, returns that property as a JSONArray object. All valid response
   * objects which contain a data collection (e.g. a collection of people)
   * must have this property.
   *
   * @param root JSONObject to query for the presence of the specific property
   * @return
   * @throws OpenSocialRequestException if property is not found in the passed
   *         object
   */
  private static JSONArray getEntryArray(JSONObject root) throws
      OpenSocialRequestException {
    String property = "";

    try {
      if (root.has("entry")) {
        property = "entry";
        return root.getJSONArray("entry");
      } else if (root.has("data")) {
        property = "list";
        return root.getJSONObject("data").getJSONArray("list");
      }
    } catch (JSONException e) {
      throw new OpenSocialRequestException(
          "\"" + property + "\" property could not be parsed as a JSON array in " +
          root.toString());
    }

    throw new OpenSocialRequestException(
        "No \"entry\" or \"list\" property in " + root.toString());
  }

  /**
   * Inspects the passed object for one of several specific properties and, if
   * found, returns that property as a JSONObject object. All valid response
   * objects which encapsulate a single data item (e.g. a person) must have
   * this property.
   *
   * @param root JSONObject to query for the presence of the specific property
   * @return
   * @throws OpenSocialRequestException if property is not found in the passed
   *         object
   */
  private static JSONObject getEntryObject(JSONObject root) throws
      OpenSocialRequestException {
    String property = "";

    try {
      if (root.has("data")) {
        property = "data";
        return root.getJSONObject(property);
      } else if (root.has("entry")) {
        property = "entry";
        return root.getJSONObject(property);
      }
    } catch (JSONException e) {
      throw new OpenSocialRequestException("\"" + property + "\" property could"
          + " not be parsed as a JSON object in " + root.toString());
    }

    throw new OpenSocialRequestException(
        "No \"entry\" or \"data\" property in " + root.toString());
  }

  /**
   * Calls a function to recursively iterates through the the properties of the
   * passed JSONObject object and returns an equivalent OpenSocialObject with
   * each property of the original object mapped to fields in the returned
   * object.
   *
   * @param entryObject Object-oriented representation of JSON response
   *        string which is transformed into and returned as an
   *        OpenSocialObject
   * @param clientClass Class of object to return, either OpenSocialObject
   *        or a subclass
   * @return
   */
  private static OpenSocialObject parseAsObject(JSONObject entryObject,
      Class<? extends OpenSocialObject> clientClass) {
    OpenSocialObject o = null;
    try {
      o = clientClass.newInstance();
    } catch (InstantiationException e) {
      throw new RuntimeException("Class not found " + clientClass);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Class not accessible " + clientClass);
    }

    Map<String, OpenSocialField> entryRepresentation =
        createObjectRepresentation(entryObject);

    for (Map.Entry<String,OpenSocialField> e : entryRepresentation.entrySet()) {
      o.setField(e.getKey(), e.getValue());
    }

    return o;
  }

  /**
   * Recursively iterates through the properties of the passed JSONObject
   * object and returns a Map of OpenSocialField objects keyed on Strings
   * representing the property values and names respectively.
   *
   * @param  o Object-oriented representation of a JSON object which is
   *         transformed into and returned as a Map of OpenSocialField
   *         objects keyed on Strings
   * @return
   */
  private static Map<String, OpenSocialField> createObjectRepresentation(
      JSONObject o) {
    HashMap<String, OpenSocialField> r = new HashMap<String, OpenSocialField>();

    Iterator<?> keys = o.keys();
    while (keys.hasNext()) {
      String key = (String) keys.next();
      String property;
      try {
        property = o.getString(key);
      } catch (JSONException e) {
        // If we can't get the object at i, ignore it
        continue;
      }

      if (property.length() < 0) {
        continue;
      }

      if (isJsonObject(property)) {
        try {
          OpenSocialField field = new OpenSocialField(true);
          field.addValue(new OpenSocialObject(createObjectRepresentation(
              o.getJSONObject(key))));
          r.put(key, field);
          continue;
        } catch (JSONException e) {
          // If this isn't an object, try to parse it as something else
        }
      }

      if (isJsonArray(property)) {
        try {
          JSONArray p = o.getJSONArray(key);
          Collection<Object> values = createArrayRepresentation(p);
          OpenSocialField field = new OpenSocialField(true);

          for (Object v : values) {
            field.addValue(v);
          }

          r.put(key, field);
          continue;
        } catch (JSONException e) {
          // If this isn't an array, try to parse it as something else
        }
      }

      // As a last resort, add in the value as a string
      OpenSocialField field = new OpenSocialField(false);
      field.addValue(unescape(property));
      r.put(key, field);
    }

    return r;
  }

  /**
   * Iterates through the objects in the passed JSONArray object, recursively
   * transforms each as needed, and returns a List of Java objects.
   *
   * @param a Object-oriented representation of a JSON array which is iterated
   *        through and returned as a List of Java objects
   * @return
   */
  private static List<Object> createArrayRepresentation(JSONArray a) {
    ArrayList<Object> r = new ArrayList<Object>(a.length());

    for (int i = 0; i < a.length(); i++) {
      String member;
      try {
        member = a.getString(i);
      } catch (JSONException e) {
        // If we can't get the object at i we will just ignore it
        continue;
      }

      if (member.length() < 0) {
        continue;
      }

      if (isJsonObject(member)) {
        try {
          r.add(new OpenSocialObject(createObjectRepresentation(a.getJSONObject(i))));
          continue;
        } catch (JSONException e) {
          // If this isn't an object, try to parse it as something else
        }
      }

      if (isJsonArray(member)) {
        try {
          JSONArray p = a.getJSONArray(i);
          for (Object v : createArrayRepresentation(p)) {
            r.add(v);
          }
          continue;
        } catch (JSONException e) {
          // If this isn't an array, try to parse it as something else
        }
      }

      // As a last resort, add in the value as a string
      r.add(member);
    }

    return r;
  }

  private static boolean isJsonArray(String str) {
    if (str != null && str.length() > 0) {
      return str.charAt(0) == '[';     
    }

    return false;
  }

  private static boolean isJsonObject(String str) {
    if (str != null && str.length() > 0) {
      return str.charAt(0) == '{';
    }

    return false;
  }

  /**
   * Escapes "{ and }" as "%7B and "%7D respectively to prevent parsing errors
   * when property values begin with { or } tokens.
   *
   * @param in String to escape
   * @return escaped String
   */
  private static String escape(String in) {
    String out = in;

    out = out.replaceAll("\"\\{", "\"%7B");
    out = out.replaceAll("\\}\"", "%7D\"");

    return out;
  }

  /**
   * Unescapes String objects previously returned from the escape method by
   * substituting { and } for %7B and %7D respectively. Called after
   * parsing to restore property values.
   *
   * @param in String to unescape
   * @return unescaped String
   */
  private static String unescape(String in) {
    String out = in;

    out = out.replaceAll("%7B", "{");
    out = out.replaceAll("%7D", "}");

    return out;
  }
}
TOP

Related Classes of org.opensocial.client.OpenSocialJsonParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.