AI2 Component  (Version nb184)
JsonUtil.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2011-2020 MIT, All rights reserved
3 // Released under the Apache License, Version 2.0
4 // http://www.apache.org/licenses/LICENSE-2.0
5 
6 package com.google.appinventor.components.runtime.util;
7 
8 import android.content.Context;
9 import android.util.Base64;
10 import android.util.Log;
11 
14 
15 import gnu.lists.FString;
16 
17 import gnu.math.IntFraction;
18 
19 import java.io.File;
20 import java.io.FileOutputStream;
21 
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map.Entry;
29 import java.util.Set;
30 import java.util.TreeSet;
31 
32 import org.json.JSONArray;
33 import org.json.JSONException;
34 import org.json.JSONObject;
35 import org.json.JSONTokener;
36 
42 public class JsonUtil {
43 
44  private static final String BINFILE_DIR = "/AppInventorBinaries";
45  private static final String LOG_TAG = "JsonUtil";
46 
50  private JsonUtil() {
51  }
52 
66  public static List<String> getStringListFromJsonArray(JSONArray jArray) throws JSONException {
67  List<String> returnList = new ArrayList<String>();
68  for (int i = 0; i < jArray.length(); i++) {
69  String val = jArray.getString(i);
70  returnList.add(val);
71  }
72  return returnList;
73  }
74 
75  @Deprecated
76  public static List<Object> getListFromJsonArray(JSONArray jsonArray) throws JSONException {
77  return getListFromJsonArray(jsonArray, false);
78  }
79 
89  public static List<Object> getListFromJsonArray(JSONArray jsonArray, boolean useDicts)
90  throws JSONException {
91  List<Object> returnList = new ArrayList<Object>();
92  for (int i = 0; i < jsonArray.length(); i++) {
93  returnList.add(convertJsonItem(jsonArray.get(i), useDicts));
94  }
95  return returnList;
96  }
97 
110  // TODO(hal): If we implement dictionaries, we'll need to decode Json
111  // objects to dictionaires instead.
112  public static List<Object> getListFromJsonObject(JSONObject jObject) throws JSONException {
113  List<Object> returnList = new ArrayList<Object>();
114  Iterator<String> keys = jObject.keys();
115 
116  List<String> keysList = new ArrayList<String>();
117  while (keys.hasNext()) {
118  keysList.add(keys.next());
119  }
120  Collections.sort(keysList);
121 
122  for (String key : keysList) {
123  List<Object> nestedList = new ArrayList<Object>();
124  nestedList.add(key);
125  nestedList.add(convertJsonItem(jObject.get(key), false));
126  returnList.add(nestedList);
127  }
128 
129  return returnList;
130  }
131 
144  public static YailDictionary getDictionaryFromJsonObject(JSONObject jsonObject)
145  throws JSONException {
146  YailDictionary result = new YailDictionary();
147 
148  // Step 1. Sort the keys
149  TreeSet<String> keys = new TreeSet<String>();
150  Iterator<String> it = jsonObject.keys();
151  while (it.hasNext()) {
152  keys.add(it.next());
153  }
154 
155  // Step 2. Populate the dictionary
156  for (String key : keys) {
157  result.put(key, convertJsonItem(jsonObject.get(key), true));
158  }
159 
160  return result;
161  }
162 
184  @Deprecated
185  public static Object convertJsonItem(Object o) throws JSONException {
186  return convertJsonItem(o, false);
187  }
188 
208  public static Object convertJsonItem(Object o, boolean useDicts) throws JSONException {
209  if (o == null) {
210  return "null";
211  }
212 
213  if (o instanceof JSONObject) {
214  if (useDicts) {
215  return getDictionaryFromJsonObject((JSONObject) o);
216  } else {
217  return getListFromJsonObject((JSONObject) o);
218  }
219  }
220 
221  if (o instanceof JSONArray) {
222  List<Object> array = getListFromJsonArray((JSONArray) o, useDicts);
223  if (useDicts) {
224  return YailList.makeList(array);
225  } else {
226  return array;
227  }
228  }
229 
230  if (o.equals(Boolean.FALSE) || (o instanceof String &&
231  ((String) o).equalsIgnoreCase("false"))) {
232  return false;
233  }
234 
235  if (o.equals(Boolean.TRUE) || (o instanceof String && ((String) o).equalsIgnoreCase("true"))) {
236  return true;
237  }
238 
239  if (o instanceof Number) {
240  return o;
241  }
242 
243  return o.toString();
244  }
245 
246  public static String getJsonRepresentation(Object value) throws JSONException {
247  if (value == null || value.equals(null)) {
248  return "null";
249  }
250  if (value instanceof FString) {
251  return JSONObject.quote(value.toString());
252  }
253  if (value instanceof YailList) {
254  return ((YailList) value).toJSONString();
255  }
256  // The Json tokener used in getObjectFromJson cannot handle
257  // fractions. So we Json encode fractions by first converting
258  // them to doubles. This is an example of value with Kawa type any
259  // being exposed to the rest of App Inventor by the value being
260  // passed to a component method, in this case TinyDB or TinyWebDB
261  // StoreValue. See the "warning" comment in runtime.scm at
262  // call-component-method.
263  if (value instanceof IntFraction) {
264  return JSONObject.numberToString((Number) ((IntFraction)value).doubleValue());
265  }
266  if (value instanceof Number) {
267  return JSONObject.numberToString((Number) value);
268  }
269  if (value instanceof Boolean) {
270  return value.toString();
271  }
272  if (value instanceof List) {
273  value = ((List)value).toArray();
274  }
275  if (value instanceof YailDictionary) {
276  StringBuilder sb = new StringBuilder();
277  YailDictionary dict = (YailDictionary) value;
278  String sep = "";
279  sb.append('{');
280  for (Entry<Object, Object> entry : (Set<Entry<Object, Object>>) dict.entrySet()) {
281  sb.append(sep);
282  sb.append(JSONObject.quote(entry.getKey().toString()));
283  sb.append(':');
284  sb.append(getJsonRepresentation(entry.getValue()));
285  sep = ",";
286  }
287  sb.append('}');
288  return sb.toString();
289  }
290  if (value.getClass().isArray()) {
291  StringBuilder sb = new StringBuilder();
292  sb.append("[");
293  String separator = "";
294  for (Object o: (Object[]) value) {
295  sb.append(separator).append(getJsonRepresentation(o));
296  separator = ",";
297  }
298  sb.append("]");
299  return sb.toString();
300  }
301  return JSONObject.quote(value.toString());
302  }
303 
316  @Deprecated
317  public static Object getObjectFromJson(String jsonString) throws JSONException {
318  return getObjectFromJson(jsonString, false);
319  }
320 
332  public static Object getObjectFromJson(String jsonString, boolean useDicts) throws JSONException {
333  if ((jsonString == null) || jsonString.equals("")) {
334  // We'd like the empty string to decode to the empty string. Form.java
335  // relies on this for the case where there's an activity result with no intent data.
336  // We handle this case explicitly since nextValue() appears to throw an error
337  // when given the empty string.
338  return "";
339  } else {
340  final Object value = (new JSONTokener(jsonString)).nextValue();
341  // Note that the JSONTokener may return a value equals() to null.
342  if (value == null || value.equals(JSONObject.NULL)) {
343  return null;
344  } else if ((value instanceof String) ||
345  (value instanceof Number) ||
346  (value instanceof Boolean)) {
347  return value;
348  } else if (value instanceof JSONArray) {
349  return getListFromJsonArray((JSONArray)value, useDicts);
350  } else if (value instanceof JSONObject) {
351  if (useDicts) {
352  return getDictionaryFromJsonObject((JSONObject) value);
353  } else {
354  return getListFromJsonObject((JSONObject) value);
355  }
356  }
357  throw new JSONException("Invalid JSON string.");
358  }
359  }
360 
375  @Deprecated
376  public static String getJsonRepresentationIfValueFileName(Object value) {
377  Log.w(LOG_TAG, "Calling deprecated function getJsonRepresentationIfValueFileName",
378  new IllegalAccessException());
380  }
381 
403  public static String getJsonRepresentationIfValueFileName(Context context, Object value) {
404  try {
405  List<String> valueList;
406  if (value instanceof String) {
407  JSONArray valueJsonList = new JSONArray((String)value);
408  valueList = getStringListFromJsonArray(valueJsonList);
409  } else if (value instanceof List) {
410  valueList = (List<String>) value;
411  } else {
412  throw new YailRuntimeError("getJsonRepresentationIfValueFileName called on unknown type",
413  value.getClass().getName());
414  }
415  if (valueList.size() == 2) {
416  if (valueList.get(0).startsWith(".")) {
417  String filename = writeFile(context, valueList.get(1), valueList.get(0).substring(1));
418  System.out.println("Filename Written: " + filename);
419  filename = filename.replace("file:/", "file:///");
420  return getJsonRepresentation(filename);
421  } else {
422  return null;
423  }
424  } else {
425  return null;
426  }
427  } catch(JSONException e) {
428  Log.e(LOG_TAG, "JSONException", e);
429  return null;
430  }
431  }
432 
445  private static String writeFile(Context context, String input, String fileExtension) {
446  FileOutputStream outStream = null;
447  try {
448  if (fileExtension.length() != 3 && fileExtension.length() != 4) {
449  throw new YailRuntimeError("File Extension must be three or four characters", "Write Error");
450  }
451  byte [] content = Base64.decode(input, Base64.DEFAULT);
452  String fullDirName = QUtil.getExternalStoragePath(context) + BINFILE_DIR;
453  File destDirectory = new File(fullDirName);
454  destDirectory.mkdirs();
455  File dest = File.createTempFile("BinFile", "." + fileExtension, destDirectory);
456  outStream = new FileOutputStream(dest);
457  outStream.write(content);
458  String retval = dest.toURI().toASCIIString();
459  trimDirectory(20, destDirectory);
460  return retval;
461  } catch (Exception e) {
462  throw new YailRuntimeError(e.getMessage(), "Write");
463  } finally {
464  IOUtils.closeQuietly(LOG_TAG, outStream);
465  }
466  }
467 
468  // keep only the last N files, where N = maxSavedFiles
469  // Written by Jeff Schiller (jis) for the BinFile Extension
470  private static void trimDirectory(int maxSavedFiles, File directory) {
471 
472  File [] files = directory.listFiles();
473 
474  Arrays.sort(files, new Comparator<File>(){
475  public int compare(File f1, File f2)
476  {
477  return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
478  } });
479 
480  int excess = files.length - maxSavedFiles;
481  for (int i = 0; i < excess; i++) {
482  files[i].delete();
483  }
484  }
485 
493  public static String encodeJsonObject(Object jsonObject) throws IllegalArgumentException {
494  try {
495  return getJsonRepresentation(jsonObject);
496  } catch (JSONException e) {
497  throw new IllegalArgumentException("jsonObject is not a legal JSON object");
498  }
499  }
500 }
501 
com.google.appinventor.components.runtime.util.YailList
Definition: YailList.java:26
com.google.appinventor.components.runtime.util.QUtil.getExternalStoragePath
static String getExternalStoragePath(Context context, boolean forcePrivate)
Definition: QUtil.java:36
com.google.appinventor.components.runtime.util.JsonUtil.getListFromJsonArray
static List< Object > getListFromJsonArray(JSONArray jsonArray)
Definition: JsonUtil.java:76
com.google.appinventor.components.runtime.util.JsonUtil.getListFromJsonArray
static List< Object > getListFromJsonArray(JSONArray jsonArray, boolean useDicts)
Definition: JsonUtil.java:89
com.google.appinventor.components
com.google.appinventor.components.runtime.util.JsonUtil.getObjectFromJson
static Object getObjectFromJson(String jsonString, boolean useDicts)
Definition: JsonUtil.java:332
com.google.appinventor.components.runtime.util.YailList.makeList
static YailList makeList(Object[] objects)
Definition: YailList.java:59
com.google.appinventor.components.runtime.util.JsonUtil
Definition: JsonUtil.java:42
com.google.appinventor.components.runtime.util.JsonUtil.getStringListFromJsonArray
static List< String > getStringListFromJsonArray(JSONArray jArray)
Definition: JsonUtil.java:66
com.google.appinventor.components.runtime.util.YailDictionary.toString
String toString()
Definition: YailDictionary.java:543
com.google.appinventor.components.runtime.util.JsonUtil.getJsonRepresentationIfValueFileName
static String getJsonRepresentationIfValueFileName(Context context, Object value)
Definition: JsonUtil.java:403
com.google.appinventor.components.runtime.util.JsonUtil.convertJsonItem
static Object convertJsonItem(Object o, boolean useDicts)
Definition: JsonUtil.java:208
com.google.appinventor.components.runtime.File
Definition: File.java:53
com.google.appinventor.components.runtime.util.QUtil
Definition: QUtil.java:18
com.google.appinventor.components.runtime.util.JsonUtil.convertJsonItem
static Object convertJsonItem(Object o)
Definition: JsonUtil.java:185
com.google.appinventor.components.runtime.util.JsonUtil.getListFromJsonObject
static List< Object > getListFromJsonObject(JSONObject jObject)
Definition: JsonUtil.java:112
com.google.appinventor.components.runtime.util.JsonUtil.encodeJsonObject
static String encodeJsonObject(Object jsonObject)
Definition: JsonUtil.java:493
com.google.appinventor.components.runtime.errors.YailRuntimeError
Definition: YailRuntimeError.java:14
com.google.appinventor.components.runtime.util.JsonUtil.getDictionaryFromJsonObject
static YailDictionary getDictionaryFromJsonObject(JSONObject jsonObject)
Definition: JsonUtil.java:144
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.util.JsonUtil.getJsonRepresentationIfValueFileName
static String getJsonRepresentationIfValueFileName(Object value)
Definition: JsonUtil.java:376
com.google.appinventor.components.runtime.util.JsonUtil.getObjectFromJson
static Object getObjectFromJson(String jsonString)
Definition: JsonUtil.java:317
com.google
com
com.google.appinventor.components.runtime.util.YailDictionary
Definition: YailDictionary.java:32
com.google.appinventor.components.runtime.util.YailList.toString
String toString()
Definition: YailList.java:180
com.google.appinventor.components.runtime.errors
Definition: ArrayIndexOutOfBoundsError.java:7
com.google.appinventor.components.runtime.util.JsonUtil.getJsonRepresentation
static String getJsonRepresentation(Object value)
Definition: JsonUtil.java:246
com.google.appinventor.components.runtime.util.YailDictionary.put
Object put(Object key, Object value)
Definition: YailDictionary.java:524
com.google.appinventor.components.runtime.Form.getActiveForm
static Form getActiveForm()
Definition: Form.java:2181
com.google.appinventor.components.runtime.Form
Definition: Form.java:126
com.google.appinventor