/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.hadoop.util; import java.util.Iterator; import java.util.Properties; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * static-ish class that builds a Properties object from JSON (or a JSON file) * * Takes a JSONObject or an InputStream and a root object path. The root object * path * provides a tree-like traversal from the JSONObject pass (or read in) before * building * the properties object * * Example JSON object: * { * 'log4j_properties': { * 'default': { * 'hadoop.root.logger': 'INFO,console', * 'hadoop.log.dir': '.', * 'hadoop.log.file': 'hadoop.log', * ... * } * }, * } * * The root object path is '.' delimited roughly translates as follows: * 'log4j_properties.default' => json['logj4_properties']['default'] * 'root.key1.key2' => json['root']['key1']['key2'] * * If the traversal path needs to resolve a JSONArray instead of a JSONObject * use indicies instead of object names, e.g.: * 'root.0.1.2' => json['root'][0][1][2] * * If a method that takes customObjectPath is called then it will first try to * return * the customObjectPath and if that doesn't exist fall back to using the * rootObjectPath. * This facilitates configs like: * { * 'log4j_properties': { * 'default': {...}, * 'TESTDFS': {...}, * } * * So 'TESTDFS' will be used if it exists, otherwise it can fall back to 'default' */ public class PropertiesFromJSON { private PropertiesFromJSON() { } /** * Uses an object path to traverse the json object and return a Properties * object * from the JSONObject (map<string, string>) object at the end of the path. If * customObjectPath * is null or isn't a valid path for the json object then rootObjectPath is * used as the path. * * @param json * @param rootObjectPath * @param customObjectPath * @return * @throws JSONException * @throws InvalidJSONPathException */ public static Properties getProperties(JSONObject json, String rootObjectPath, String customObjectPath) throws JSONException, InvalidJSONPathException { if (customObjectPath != null && !customObjectPath.equals("")) { try { return getPropertiesFromTraversal(json, customObjectPath); } catch (InvalidJSONPathException e) { // fall through to using rootObjectPath } } return getPropertiesFromTraversal(json, rootObjectPath); } /** * Uses an object path to traverse the json array and return a Properties * object * from the JSONObject (map<string, string>) object at the end of the path. If * customObjectPath * is null or isn't a valid path for the json object then rootObjectPath is * used as the path. * * @param json * @param rootObjectPath * @param customObjectPath * @return * @throws JSONException * @throws InvalidJSONPathException */ public static Properties getProperties(JSONArray json, String rootObjectPath, String customObjectPath) throws JSONException, InvalidJSONPathException { if (customObjectPath != null && !customObjectPath.equals("")) { try { return getPropertiesFromTraversal(json, customObjectPath); } catch (InvalidJSONPathException e) { // fall through to using rootObjectPath // } } return getPropertiesFromTraversal(json, rootObjectPath); } private static Properties getPropertiesFromTraversal(JSONObject json, String objectPath) throws JSONException, InvalidJSONPathException { if (objectPath != null && !objectPath.equals("")) { if (objectPath.startsWith("$.")) { return getPropertiesFromTraversal(json, objectPath.substring(2)); } int nextDotIndex = objectPath.indexOf("."); String nextObjectPath = null; String remainderObjectPath; if (nextDotIndex < 0) { nextObjectPath = objectPath; remainderObjectPath = null; } else { nextObjectPath = objectPath.substring(0, nextDotIndex); remainderObjectPath = objectPath.substring(nextDotIndex + 1); } if (!json.has(nextObjectPath)) { throw new InvalidJSONPathException("'" + nextObjectPath + "' cannot be found in " + json.toString()); } JSONObject nextJSONObject = json.optJSONObject(nextObjectPath); if (nextJSONObject != null) { return getPropertiesFromTraversal(nextJSONObject, remainderObjectPath); } JSONArray nextJSONArray = json.optJSONArray(nextObjectPath); if (nextJSONArray != null) { return getPropertiesFromTraversal(nextJSONArray, remainderObjectPath); } throw new InvalidJSONPathException("'" + nextObjectPath + "' is neither a JSONObject nor a JSONArray: " + json.toString()); } Properties properties = new Properties(); String key = null; for (Iterator<?> keys = json.keys(); keys.hasNext(); key = (String) keys.next()) { try { properties.setProperty(key, json.getString(key)); } catch (JSONException e) { } } return properties; } private static Properties getPropertiesFromTraversal(JSONArray json, String objectPath) throws JSONException, InvalidJSONPathException { if (objectPath == null || objectPath.equals("")) { throw new InvalidJSONPathException("no index provided to reference JSONArray " + json.toString()); } int nextDotIndex = objectPath.indexOf("."); int nextObjectIndex; String nextObjectPath = null; String remainderObjectPath; try { if (nextDotIndex < 0) { nextObjectPath = objectPath; remainderObjectPath = null; } else { nextObjectPath = objectPath.substring(0, nextDotIndex - 1); remainderObjectPath = objectPath.substring(nextDotIndex + 1); } nextObjectIndex = Integer.valueOf(nextObjectPath); } catch (NumberFormatException nfe) { throw new InvalidJSONPathException("'" + nextObjectPath + "' is not a valid integer index for " + json.toString()); } if (nextObjectIndex < 0 || json.length() < nextObjectIndex) { throw new InvalidJSONPathException("'" + nextObjectIndex + "' is larger than the size of JSONArray " + json.toString()); } JSONObject nextJSONObject = json.optJSONObject(nextObjectIndex); if (nextJSONObject != null) { return getPropertiesFromTraversal(nextJSONObject, remainderObjectPath); } JSONArray nextJSONArray = json.optJSONArray(nextObjectIndex); if (nextJSONArray != null) { return getPropertiesFromTraversal(nextJSONArray, remainderObjectPath); } throw new InvalidJSONPathException("'" + nextObjectPath + "' is neither a JSONObject nor a JSONArray: " + json.toString()); } }