Skip to content

Optional type conversion for XML reading #253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 104 additions & 21 deletions JSONML.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@ of this software and associated documentation files (the "Software"), to deal
* @version 2016-01-30
*/
public class JSONML {

/**
* Parse XML values and store them in a JSONArray.
* @param x The XMLTokener containing the source string.
* @param arrayForm true if array form, false if object form.
* @param ja The JSONArray that is containing the current tag or null
* if we are at the outermost level.
* @param keepStrings Don't type-convert text nodes and attibute values
* @return A JSONArray if the value is the outermost tag, otherwise null.
* @throws JSONException
*/
private static Object parse(
XMLTokener x,
boolean arrayForm,
JSONArray ja
JSONArray ja,
boolean keepStrings
) throws JSONException {
String attribute;
char c;
Expand Down Expand Up @@ -174,7 +175,7 @@ private static Object parse(
if (!(token instanceof String)) {
throw x.syntaxError("Missing value");
}
newjo.accumulate(attribute, JSONObject.stringToValue((String)token));
newjo.accumulate(attribute, keepStrings ? token :JSONObject.stringToValue((String)token));
token = null;
} else {
newjo.accumulate(attribute, "");
Expand All @@ -193,9 +194,8 @@ private static Object parse(
if (ja == null) {
if (arrayForm) {
return newja;
} else {
return newjo;
}
return newjo;
}

// Content, between <...> and </...>
Expand All @@ -204,7 +204,7 @@ private static Object parse(
if (token != XML.GT) {
throw x.syntaxError("Misshaped tag");
}
closeTag = (String)parse(x, arrayForm, newja);
closeTag = (String)parse(x, arrayForm, newja, keepStrings);
if (closeTag != null) {
if (!closeTag.equals(tagName)) {
throw x.syntaxError("Mismatched '" + tagName +
Expand All @@ -217,17 +217,16 @@ private static Object parse(
if (ja == null) {
if (arrayForm) {
return newja;
} else {
return newjo;
}
return newjo;
}
}
}
}
} else {
if (ja != null) {
ja.put(token instanceof String
? JSONObject.stringToValue((String)token)
? keepStrings ? token :JSONObject.stringToValue((String)token)
: token);
}
}
Expand All @@ -245,10 +244,32 @@ private static Object parse(
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
* @param string The source string.
* @return A JSONArray containing the structured data from the XML string.
* @throws JSONException
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(String string) throws JSONException {
return toJSONArray(new XMLTokener(string));
return (JSONArray)parse(new XMLTokener(string), true, null, false);
}


/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONArray using the JsonML transform. Each XML tag is represented as
* a JSONArray in which the first element is the tag name. If the tag has
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child tags.
* As opposed to toJSONArray this method does not attempt to convert
* any text node or attribute value to any type
* but just leaves it as a string.
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
* @param string The source string.
* @param keepStrings If true, then values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONArray containing the structured data from the XML string.
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(String string, boolean keepStrings) throws JSONException {
return (JSONArray)parse(new XMLTokener(string), true, null, keepStrings);
}


Expand All @@ -259,16 +280,76 @@ public static JSONArray toJSONArray(String string) throws JSONException {
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child content and tags.
* As opposed to toJSONArray this method does not attempt to convert
* any text node or attribute value to any type
* but just leaves it as a string.
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
* @param x An XMLTokener.
* @param keepStrings If true, then values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONArray containing the structured data from the XML string.
* @throws JSONException
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings) throws JSONException {
return (JSONArray)parse(x, true, null, keepStrings);
}


/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONArray using the JsonML transform. Each XML tag is represented as
* a JSONArray in which the first element is the tag name. If the tag has
* attributes, then the second element will be JSONObject containing the
* name/value pairs. If the tag contains children, then strings and
* JSONArrays will represent the child content and tags.
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
* @param x An XMLTokener.
* @return A JSONArray containing the structured data from the XML string.
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
return (JSONArray)parse(x, true, null);
return (JSONArray)parse(x, true, null, false);
}


/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
* a JSONObject with a "tagName" property. If the tag has attributes, then
* the attributes will be in the JSONObject as properties. If the tag
* contains children, the object will have a "childNodes" property which
* will be an array of strings and JsonML JSONObjects.

* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
* @param string The XML source text.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(String string) throws JSONException {
return (JSONObject)parse(new XMLTokener(string), false, null, false);
}


/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
* a JSONObject with a "tagName" property. If the tag has attributes, then
* the attributes will be in the JSONObject as properties. If the tag
* contains children, the object will have a "childNodes" property which
* will be an array of strings and JsonML JSONObjects.

* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
* @param string The XML source text.
* @param keepStrings If true, then values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException {
return (JSONObject)parse(new XMLTokener(string), false, null, keepStrings);
}


/**
* Convert a well-formed (but not necessarily valid) XML string into a
* JSONObject using the JsonML transform. Each XML tag is represented as
Expand All @@ -280,10 +361,10 @@ public static JSONArray toJSONArray(XMLTokener x) throws JSONException {
* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
* @param x An XMLTokener of the XML source text.
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
return (JSONObject)parse(x, false, null);
return (JSONObject)parse(x, false, null, false);
}


Expand All @@ -296,20 +377,22 @@ public static JSONObject toJSONObject(XMLTokener x) throws JSONException {
* will be an array of strings and JsonML JSONObjects.

* Comments, prologs, DTDs, and <code>&lt;[ [ ]]></code> are ignored.
* @param string The XML source text.
* @param x An XMLTokener of the XML source text.
* @param keepStrings If true, then values will not be coerced into boolean
* or numeric values and will instead be left as strings
* @return A JSONObject containing the structured data from the XML string.
* @throws JSONException
* @throws JSONException Thrown on error converting to a JSONObject
*/
public static JSONObject toJSONObject(String string) throws JSONException {
return toJSONObject(new XMLTokener(string));
public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings) throws JSONException {
return (JSONObject)parse(x, false, null, keepStrings);
}


/**
* Reverse the JSONML transformation, making an XML text from a JSONArray.
* @param ja A JSONArray.
* @return An XML string.
* @throws JSONException
* @throws JSONException Thrown on error converting to a string
*/
public static String toString(JSONArray ja) throws JSONException {
int i;
Expand Down Expand Up @@ -393,7 +476,7 @@ public static String toString(JSONArray ja) throws JSONException {
* The other properties are attributes with string values.
* @param jo A JSONObject.
* @return An XML string.
* @throws JSONException
* @throws JSONException Thrown on error converting to a string
*/
public static String toString(JSONObject jo) throws JSONException {
StringBuilder sb = new StringBuilder();
Expand Down
19 changes: 17 additions & 2 deletions JSONTokener.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public JSONTokener(Reader reader) {
* Construct a JSONTokener from an InputStream.
* @param inputStream The source.
*/
public JSONTokener(InputStream inputStream) throws JSONException {
public JSONTokener(InputStream inputStream) {
this(new InputStreamReader(inputStream));
}

Expand All @@ -90,6 +90,8 @@ public JSONTokener(String s) {
* Back up one character. This provides a sort of lookahead capability,
* so that you can test for a digit or letter before attempting to parse
* the next number or identifier.
* @throws JSONException Thrown if trying to step back more than 1 step
* or if already at the start of the string
*/
public void back() throws JSONException {
if (this.usePrevious || this.index <= 0) {
Expand Down Expand Up @@ -121,6 +123,9 @@ public static int dehexchar(char c) {
return -1;
}

/**
* @return true if at the end of the file and we didn't step back
*/
public boolean end() {
return this.eof && !this.usePrevious;
}
Expand All @@ -130,6 +135,8 @@ public boolean end() {
* Determine if the source string still contains characters that next()
* can consume.
* @return true if not yet at the end of the source.
* @throws JSONException thrown if there is an error stepping forward
* or backward while checking for more data.
*/
public boolean more() throws JSONException {
this.next();
Expand All @@ -145,6 +152,7 @@ public boolean more() throws JSONException {
* Get the next character in the source string.
*
* @return The next character, or 0 if past the end of the source string.
* @throws JSONException Thrown if there is an error reading the source string.
*/
public char next() throws JSONException {
int c;
Expand Down Expand Up @@ -225,7 +233,7 @@ public String next(int n) throws JSONException {

/**
* Get the next char in the string, skipping whitespace.
* @throws JSONException
* @throws JSONException Thrown if there is an error reading the source string.
* @return A character, or 0 if there are no more characters.
*/
public char nextClean() throws JSONException {
Expand Down Expand Up @@ -309,6 +317,8 @@ public String nextString(char quote) throws JSONException {
* end of line, whichever comes first.
* @param delimiter A delimiter character.
* @return A string.
* @throws JSONException Thrown if there is an error while searching
* for the delimiter
*/
public String nextTo(char delimiter) throws JSONException {
StringBuilder sb = new StringBuilder();
Expand All @@ -330,6 +340,8 @@ public String nextTo(char delimiter) throws JSONException {
* characters or the end of line, whichever comes first.
* @param delimiters A set of delimiter characters.
* @return A string, trimmed.
* @throws JSONException Thrown if there is an error while searching
* for the delimiter
*/
public String nextTo(String delimiters) throws JSONException {
char c;
Expand Down Expand Up @@ -401,6 +413,8 @@ public Object nextValue() throws JSONException {
* @param to A character to skip to.
* @return The requested character, or zero if the requested character
* is not found.
* @throws JSONException Thrown if there is an error while searching
* for the to character
*/
public char skipTo(char to) throws JSONException {
char c;
Expand Down Expand Up @@ -453,6 +467,7 @@ public JSONException syntaxError(String message, Throwable causedBy) {
*
* @return " at {index} [character {character} line {line}]"
*/
@Override
public String toString() {
return " at " + this.index + " [character " + this.character + " line " +
this.line + "]";
Expand Down
Loading