[
https://issues.apache.org/jira/browse/PHOENIX-174?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14508389#comment-14508389
]
ASF GitHub Bot commented on PHOENIX-174:
----------------------------------------
Github user JamesRTaylor commented on a diff in the pull request:
https://github.com/apache/phoenix/pull/75#discussion_r28934484
--- Diff:
phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java ---
@@ -0,0 +1,216 @@
+package org.apache.phoenix.schema.json;
+
+import java.io.IOException;
+import java.sql.SQLException;
+
+import org.apache.hadoop.hbase.util.Bytes;
+import org.codehaus.jackson.JsonFactory;
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonParser.Feature;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.node.ValueNode;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * The {@link PhoenixJson} wraps json and uses Jackson library to parse
and traverse the json. It
+ * should be used to represent the JSON data type and also should be used
to parse Json data and
+ * read the value from it. It always conside the last value if same key
exist more than once.
+ */
+public class PhoenixJson {
+ private final JsonNode node;
+ private String jsonAsString;
+
+ /**
+ * Static Factory method to get an {@link PhoenixJson} object. It also
validates the json and
+ * throws {@link JsonParseException} if it is invalid with line number
and character, which
+ * should be wrapped in {@link SQLException} by caller.
+ * @param data Buffer that contains data to parse
+ * @param offset Offset of the first data byte within buffer
+ * @param length Length of contents to parse within buffer
+ * @return {@link PhoenixJson}.
+ * @throws JsonParseException
+ * @throws IOException
+ */
+ public static PhoenixJson getPhoenixJson(byte[] jsonData, int offset,
int length)
+ throws JsonParseException, IOException {
+ JsonFactory jsonFactory = new JsonFactory();
+ JsonParser jsonParser = jsonFactory.createJsonParser(jsonData,
offset, length);
+ jsonParser.configure(Feature.ALLOW_COMMENTS, true);
+ ObjectMapper objectMapper = new ObjectMapper();
+ try {
+ JsonNode rootNode = objectMapper.readTree(jsonParser);
+ PhoenixJson phoenixJson = new PhoenixJson(rootNode);
+
+ /*
+ * input data has been stored as it is, since some data is
lost when json parser runs,
+ * for example if a JSON object within the value contains the
same key more than once
+ * then only last one is stored rest all of them are ignored,
which will defy the
+ * contract of PJsonDataType of keeping user data as it is.
+ */
+ phoenixJson.setJsonAsString(Bytes.toString(jsonData, offset,
length));
+ return phoenixJson;
+ } finally {
+ jsonParser.close();
+ }
+
+ }
+
+ /* Default for unit testing */PhoenixJson(final JsonNode node) {
+ Preconditions.checkNotNull(node, "root node cannot be null for
json");
+ this.node = node;
+ }
+
+ /**
+ * Get {@link PhoenixJson} for a given json paths. For example :
+ * <p>
+ * <code>
+ * {"f2":{"f3":1},"f4":{"f5":99,"f6":{"f7":"2"}}}'
+ * </code>
+ * <p>
+ * for this source json, if we want to know the json at path
{'f4','f6'} it will return
+ * {@link PhoenixJson} object for json {"f7":"2"}. It always returns
the last key if same key
+ * exist more than once.
+ * <p>
+ * If the given path is unreachable then it throws {@link
PhoenixJsonException} with the message
+ * having information about not found path. It is caller
responsibility to wrap it in
+ * {@link SQLException} or catch it and return null to client.
+ * @param paths {@link String []} of path in the same order as they
appear in json.
+ * @return {@link PhoenixJson} for the json against @paths.
+ * @throws PhoenixJsonException
+ */
+ public PhoenixJson getPhoenixJson(String[] paths) throws
PhoenixJsonException {
+ JsonNode node = this.node;
+ for (String path : paths) {
+ JsonNode nodeTemp = null;
+ if (node.isArray()) {
+ try {
+ int index = Integer.parseInt(path);
+ nodeTemp = node.path(index);
+ } catch (NumberFormatException nfe) {
+ throw new PhoenixJsonException("path: " + path + " not
found", nfe);
+ }
+ } else {
+ nodeTemp = node.path(path);
+ }
+ if (nodeTemp == null || nodeTemp.isMissingNode()) {
+ throw new PhoenixJsonException("path: " + path + " not
found");
+ }
+ node = nodeTemp;
+ }
+ return new PhoenixJson(node);
+ }
+
+ /**
+ * Get {@link PhoenixJson} for a given json paths. For example :
+ * <p>
+ * <code>
+ * {"f2":{"f3":1},"f4":{"f5":99,"f6":{"f7":"2"}}}'
+ * </code>
+ * <p>
+ * for this source json, if we want to know the json at path
{'f4','f6'} it will return
+ * {@link PhoenixJson} object for json {"f7":"2"}. It always returns
the last key if same key
+ * exist more than once.
+ * <p>
+ * If the given path is unreachable then it return null.
+ * @param paths {@link String []} of path in the same order as they
appear in json.
+ * @return {@link PhoenixJson} for the json against @paths.
+ */
+ public PhoenixJson getNullablePhoenixJson(String[] paths) {
+ JsonNode node = this.node;
+ for (String path : paths) {
+ JsonNode nodeTemp = null;
+ if (node.isArray()) {
+ try {
+ int index = Integer.parseInt(path);
+ nodeTemp = node.path(index);
+ } catch (NumberFormatException nfe) {
+ return null;
+ }
+ } else {
+ nodeTemp = node.path(path);
+ }
+ if (nodeTemp == null || nodeTemp.isMissingNode()) {
+ return null;
+ }
+ node = nodeTemp;
+ }
+ return new PhoenixJson(node);
+ }
+
+ /**
+ * Serialize the current {@link PhoenixJson} to String. Its required
for
+ * json_extract_path_text(). If we just return node.toString() it will
wrap String value in
+ * double quote which is not the expectation, hence avoiding calling
toString() on
+ * {@link JsonNode} until PhoenixJson represent a Json Array or
container for Json object. If
+ * PhoenixJson just represent a {@link ValueNode} then it should
return value returned from
+ * objects toString().
+ */
+ public String serializeToString() {
+ if (this.node == null || this.node.isNull()) {
+ return null;
+ } else if (this.node.isValueNode()) {
+
+ if (this.node.isNumber()) {
+ return this.node.getNumberValue().toString();
+ } else if (this.node.isBoolean()) {
+ return String.valueOf(this.node.getBooleanValue());
+ } else if (this.node.isTextual()) {
+ return this.node.getTextValue();
+ } else {
+ return getJsonAsString();
+ }
+ } else if (this.node.isArray()) {
+ return getJsonAsString();
+ } else if (this.node.isContainerNode()) {
+ return getJsonAsString();
+ }
+
+ return null;
+
+ }
+
+ @Override
+ public String toString() {
+ if (this.jsonAsString == null && this.node != null) {
--- End diff --
Will this be called often? Not sure if there's a potential threading issue.
If not called often, might not want to cache the string.
> Zookeeper parameter in Phoenix JDBC URL should be optional as it can be
> specified in hbase-site.xml
> ---------------------------------------------------------------------------------------------------
>
> Key: PHOENIX-174
> URL: https://issues.apache.org/jira/browse/PHOENIX-174
> Project: Phoenix
> Issue Type: Task
> Affects Versions: 1.1
> Reporter: mujtaba
> Labels: enhancement
>
> Currently, value from HBase zookeeper/port specified in Phoenix JDBC URL
> overrides the value specified in hbase-site.xml. Override is fine, but it
> should use value specified in hbase-site.xml if no value is specified in
> phoenix JDBC URL i.e. to make this parameter optional.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)