diff --git a/src/main/java/org/json/CDL.java b/src/main/java/org/json/CDL.java index 848831d..251386b 100644 --- a/src/main/java/org/json/CDL.java +++ b/src/main/java/org/json/CDL.java @@ -5,15 +5,15 @@ Public Domain. */ /** - * This provides static methods to convert comma delimited text into a - * JSONArray, and to convert a JSONArray into comma delimited text. Comma + * This provides static methods to convert comma (or otherwise) delimited text into a + * JSONArray, and to convert a JSONArray into comma (or otherwise) delimited text. Comma * delimited text is a very popular format for data interchange. It is * understood by most database, spreadsheet, and organizer programs. *

* Each row of text represents a row in a table or a data record. Each row * ends with a NEWLINE character. Each row contains one or more values. * Values are separated by commas. A value can contain any character except - * for comma, unless is is wrapped in single quotes or double quotes. + * for comma, unless it is wrapped in single quotes or double quotes. *

* The first row usually contains the names of the columns. *

@@ -29,50 +29,48 @@ public class CDL { * Get the next value. The value can be wrapped in quotes. The value can * be empty. * @param x A JSONTokener of the source text. + * @param delimiter used in the file * @return The value string, or null if empty. * @throws JSONException if the quoted string is badly formed. */ - private static String getValue(JSONTokener x) throws JSONException { + private static String getValue(JSONTokener x, char delimiter) throws JSONException { char c; char q; StringBuilder sb; do { c = x.next(); } while (c == ' ' || c == '\t'); - switch (c) { - case 0: - return null; - case '"': - case '\'': - q = c; - sb = new StringBuilder(); - for (;;) { - c = x.next(); - if (c == q) { - //Handle escaped double-quote - char nextC = x.next(); - if(nextC != '\"') { - // if our quote was the end of the file, don't step - if(nextC > 0) { - x.back(); - } - break; - } - } - if (c == 0 || c == '\n' || c == '\r') { - throw x.syntaxError("Missing close quote '" + q + "'."); - } - sb.append(c); - } - return sb.toString(); - case ',': - x.back(); - return ""; - default: - x.back(); - return x.nextTo(','); - } - } + if (c == 0) { + return null; + } else if (c == '"' || c == '\'') { + q = c; + sb = new StringBuilder(); + for (;;) { + c = x.next(); + if (c == q) { + //Handle escaped double-quote + char nextC = x.next(); + if (nextC != '\"') { + // if our quote was the end of the file, don't step + if (nextC > 0) { + x.back(); + } + break; + } + } + if (c == 0 || c == '\n' || c == '\r') { + throw x.syntaxError("Missing close quote '" + q + "'."); + } + sb.append(c); + } + return sb.toString(); + } else if (c == delimiter) { + x.back(); + return ""; + } + x.back(); + return x.nextTo(delimiter); + } /** * Produce a JSONArray of strings from a row of comma delimited values. @@ -81,17 +79,25 @@ public class CDL { * @throws JSONException if a called function fails */ public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException { + return rowToJSONArray(x, ','); + } + + /** + * Same as {@link #rowToJSONArray(JSONTokener)}, but with a custom delimiter. + * @see #rowToJSONArray(JSONTokener) + */ + public static JSONArray rowToJSONArray(JSONTokener x, char delimiter) throws JSONException { JSONArray ja = new JSONArray(); for (;;) { - String value = getValue(x); + String value = getValue(x,delimiter); char c = x.next(); if (value == null || - (ja.length() == 0 && value.length() == 0 && c != ',')) { + (ja.length() == 0 && value.length() == 0 && c != delimiter)) { return null; } ja.put(value); for (;;) { - if (c == ',') { + if (c == delimiter) { break; } if (c != ' ') { @@ -116,9 +122,17 @@ public class CDL { * @return A JSONObject combining the names and values. * @throws JSONException if a called function fails */ - public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) - throws JSONException { - JSONArray ja = rowToJSONArray(x); + public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) throws JSONException { + return rowToJSONObject(names, x, ','); + } + + /** + * Same as {@link #rowToJSONObject(JSONArray, JSONTokener)}, but with a custom {@code delimiter}. + * + * @see #rowToJSONObject(JSONArray, JSONTokener) + */ + public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x, char delimiter) throws JSONException { + JSONArray ja = rowToJSONArray(x, delimiter); return ja != null ? ja.toJSONObject(names) : null; } @@ -130,15 +144,23 @@ public class CDL { * @return A string ending in NEWLINE. */ public static String rowToString(JSONArray ja) { + return rowToString(ja, ','); + } + + /** + * Same as {@link #rowToString(JSONArray)}, but with a custom delimiter. + * @see #rowToString(JSONArray) + */ + public static String rowToString(JSONArray ja, char delimiter) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < ja.length(); i += 1) { if (i > 0) { - sb.append(','); + sb.append(delimiter); } Object object = ja.opt(i); if (object != null) { String string = object.toString(); - if (string.length() > 0 && (string.indexOf(',') >= 0 || + if (string.length() > 0 && (string.indexOf(delimiter) >= 0 || string.indexOf('\n') >= 0 || string.indexOf('\r') >= 0 || string.indexOf(0) >= 0 || string.charAt(0) == '"')) { sb.append('"'); @@ -167,7 +189,15 @@ public class CDL { * @throws JSONException if a called function fails */ public static JSONArray toJSONArray(String string) throws JSONException { - return toJSONArray(new JSONTokener(string)); + return toJSONArray(string, ','); + } + + /** + * Same as {@link #toJSONArray(String)}, but with a custom delimiter. + * @see #toJSONArray(String) + */ + public static JSONArray toJSONArray(String string, char delimiter) throws JSONException { + return toJSONArray(new JSONTokener(string), delimiter); } /** @@ -178,7 +208,15 @@ public class CDL { * @throws JSONException if a called function fails */ public static JSONArray toJSONArray(JSONTokener x) throws JSONException { - return toJSONArray(rowToJSONArray(x), x); + return toJSONArray(x, ','); + } + + /** + * Same as {@link #toJSONArray(JSONTokener)}, but with a custom delimiter. + * @see #toJSONArray(JSONTokener) + */ + public static JSONArray toJSONArray(JSONTokener x, char delimiter) throws JSONException { + return toJSONArray(rowToJSONArray(x, delimiter), x, delimiter); } /** @@ -189,9 +227,16 @@ public class CDL { * @return A JSONArray of JSONObjects. * @throws JSONException if a called function fails */ - public static JSONArray toJSONArray(JSONArray names, String string) - throws JSONException { - return toJSONArray(names, new JSONTokener(string)); + public static JSONArray toJSONArray(JSONArray names, String string) throws JSONException { + return toJSONArray(names, string, ','); + } + + /** + * Same as {@link #toJSONArray(JSONArray, String)}, but with a custom delimiter. + * @see #toJSONArray(JSONArray, String) + */ + public static JSONArray toJSONArray(JSONArray names, String string, char delimiter) throws JSONException { + return toJSONArray(names, new JSONTokener(string), delimiter); } /** @@ -202,14 +247,21 @@ public class CDL { * @return A JSONArray of JSONObjects. * @throws JSONException if a called function fails */ - public static JSONArray toJSONArray(JSONArray names, JSONTokener x) - throws JSONException { + public static JSONArray toJSONArray(JSONArray names, JSONTokener x) throws JSONException { + return toJSONArray(names, x, ','); + } + + /** + * Same as {@link #toJSONArray(JSONArray, JSONTokener)}, but with a custom delimiter. + * @see #toJSONArray(JSONArray, JSONTokener) + */ + public static JSONArray toJSONArray(JSONArray names, JSONTokener x, char delimiter) throws JSONException { if (names == null || names.length() == 0) { return null; } JSONArray ja = new JSONArray(); for (;;) { - JSONObject jo = rowToJSONObject(names, x); + JSONObject jo = rowToJSONObject(names, x, delimiter); if (jo == null) { break; } @@ -231,11 +283,19 @@ public class CDL { * @throws JSONException if a called function fails */ public static String toString(JSONArray ja) throws JSONException { + return toString(ja, ','); + } + + /** + * Same as {@link #toString(JSONArray)}, but with a custom delimiter. + * @see #toString(JSONArray) + */ + public static String toString(JSONArray ja, char delimiter) throws JSONException { JSONObject jo = ja.optJSONObject(0); if (jo != null) { JSONArray names = jo.names(); if (names != null) { - return rowToString(names) + toString(names, ja); + return rowToString(names, delimiter) + toString(names, ja, delimiter); } } return null; @@ -250,8 +310,15 @@ public class CDL { * @return A comma delimited text. * @throws JSONException if a called function fails */ - public static String toString(JSONArray names, JSONArray ja) - throws JSONException { + public static String toString(JSONArray names, JSONArray ja) throws JSONException { + return toString(names, ja, ','); + } + + /** + * Same as {@link #toString(JSONArray,JSONArray)}, but with a custom delimiter. + * @see #toString(JSONArray,JSONArray) + */ + public static String toString(JSONArray names, JSONArray ja, char delimiter) throws JSONException { if (names == null || names.length() == 0) { return null; } @@ -259,7 +326,7 @@ public class CDL { for (int i = 0; i < ja.length(); i += 1) { JSONObject jo = ja.optJSONObject(i); if (jo != null) { - sb.append(rowToString(jo.toJSONArray(names))); + sb.append(rowToString(jo.toJSONArray(names), delimiter)); } } return sb.toString(); diff --git a/src/test/java/org/json/junit/CDLTest.java b/src/test/java/org/json/junit/CDLTest.java index f3364fb..cc3da29 100644 --- a/src/test/java/org/json/junit/CDLTest.java +++ b/src/test/java/org/json/junit/CDLTest.java @@ -24,14 +24,13 @@ public class CDLTest { * String of lines where the column names are in the first row, * and all subsequent rows are values. All keys and values should be legal. */ - String lines = new String( - "Col 1, Col 2, \tCol 3, Col 4, Col 5, Col 6, Col 7\n" + - "val1, val2, val3, val4, val5, val6, val7\n" + - "1, 2, 3, 4\t, 5, 6, 7\n" + - "true, false, true, true, false, false, false\n" + - "0.23, 57.42, 5e27, -234.879, 2.34e5, 0.0, 9e-3\n" + - "\"va\tl1\", \"v\bal2\", \"val3\", \"val\f4\", \"val5\", va\'l6, val7\n" - ); + private static final String LINES = "Col 1, Col 2, \tCol 3, Col 4, Col 5, Col 6, Col 7\n" + + "val1, val2, val3, val4, val5, val6, val7\n" + + "1, 2, 3, 4\t, 5, 6, 7\n" + + "true, false, true, true, false, false, false\n" + + "0.23, 57.42, 5e27, -234.879, 2.34e5, 0.0, 9e-3\n" + + "\"va\tl1\", \"v\bal2\", \"val3\", \"val\f4\", \"val5\", va'l6, val7\n"; + /** * CDL.toJSONArray() adds all values as strings, with no filtering or @@ -39,12 +38,11 @@ public class CDLTest { * values all must be quoted in the cases where the JSONObject parsing * might normally convert the value into a non-string. */ - String expectedLines = new String( - "[{Col 1:val1, Col 2:val2, Col 3:val3, Col 4:val4, Col 5:val5, Col 6:val6, Col 7:val7}, "+ - "{Col 1:\"1\", Col 2:\"2\", Col 3:\"3\", Col 4:\"4\", Col 5:\"5\", Col 6:\"6\", Col 7:\"7\"}, "+ - "{Col 1:\"true\", Col 2:\"false\", Col 3:\"true\", Col 4:\"true\", Col 5:\"false\", Col 6:\"false\", Col 7:\"false\"}, "+ - "{Col 1:\"0.23\", Col 2:\"57.42\", Col 3:\"5e27\", Col 4:\"-234.879\", Col 5:\"2.34e5\", Col 6:\"0.0\", Col 7:\"9e-3\"}, "+ - "{Col 1:\"va\tl1\", Col 2:\"v\bal2\", Col 3:val3, Col 4:\"val\f4\", Col 5:val5, Col 6:va\'l6, Col 7:val7}]"); + private static final String EXPECTED_LINES = "[{Col 1:val1, Col 2:val2, Col 3:val3, Col 4:val4, Col 5:val5, Col 6:val6, Col 7:val7}, " + + "{Col 1:\"1\", Col 2:\"2\", Col 3:\"3\", Col 4:\"4\", Col 5:\"5\", Col 6:\"6\", Col 7:\"7\"}, " + + "{Col 1:\"true\", Col 2:\"false\", Col 3:\"true\", Col 4:\"true\", Col 5:\"false\", Col 6:\"false\", Col 7:\"false\"}, " + + "{Col 1:\"0.23\", Col 2:\"57.42\", Col 3:\"5e27\", Col 4:\"-234.879\", Col 5:\"2.34e5\", Col 6:\"0.0\", Col 7:\"9e-3\"}, " + + "{Col 1:\"va\tl1\", Col 2:\"v\bal2\", Col 3:val3, Col 4:\"val\f4\", Col 5:val5, Col 6:va'l6, Col 7:val7}]"; /** * Attempts to create a JSONArray from a null string. @@ -194,8 +192,7 @@ public class CDLTest { public void emptyString() { String emptyStr = ""; JSONArray jsonArray = CDL.toJSONArray(emptyStr); - assertTrue("CDL should return null when the input string is empty", - jsonArray == null); + assertNull("CDL should return null when the input string is empty", jsonArray); } /** @@ -254,7 +251,7 @@ public class CDLTest { jsonObject.put("Col \r1", "V1"); // \r will be filtered from value jsonObject.put("Col 2", "V2\r"); - assertTrue("expected length should be 1",jsonArray.length() == 1); + assertEquals("expected length should be 1", 1, jsonArray.length()); String cdlStr = CDL.toString(jsonArray); jsonObject = jsonArray.getJSONObject(0); assertTrue(cdlStr.contains("\"Col 1\"")); @@ -268,8 +265,15 @@ public class CDLTest { */ @Test public void textToJSONArray() { - JSONArray jsonArray = CDL.toJSONArray(this.lines); - JSONArray expectedJsonArray = new JSONArray(this.expectedLines); + JSONArray jsonArray = CDL.toJSONArray(LINES); + JSONArray expectedJsonArray = new JSONArray(EXPECTED_LINES); + Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray); + } + @Test + public void textToJSONArrayPipeDelimited() { + char delimiter = '|'; + JSONArray jsonArray = CDL.toJSONArray(LINES.replaceAll(",", String.valueOf(delimiter)), delimiter); + JSONArray expectedJsonArray = new JSONArray(EXPECTED_LINES); Util.compareActualVsExpectedJsonArrays(jsonArray, expectedJsonArray); } @@ -293,10 +297,24 @@ public class CDLTest { */ @Test public void textToJSONArrayAndBackToString() { - JSONArray jsonArray = CDL.toJSONArray(this.lines); + JSONArray jsonArray = CDL.toJSONArray(LINES); String jsonStr = CDL.toString(jsonArray); JSONArray finalJsonArray = CDL.toJSONArray(jsonStr); - JSONArray expectedJsonArray = new JSONArray(this.expectedLines); + JSONArray expectedJsonArray = new JSONArray(EXPECTED_LINES); + Util.compareActualVsExpectedJsonArrays(finalJsonArray, expectedJsonArray); + } + + /** + * Create a JSONArray from a string of lines, + * then convert to string and then back to JSONArray + * with a custom delimiter + */ + @Test + public void textToJSONArrayAndBackToStringCustomDelimiter() { + JSONArray jsonArray = CDL.toJSONArray(LINES, ','); + String jsonStr = CDL.toString(jsonArray, ';'); + JSONArray finalJsonArray = CDL.toJSONArray(jsonStr, ';'); + JSONArray expectedJsonArray = new JSONArray(EXPECTED_LINES); Util.compareActualVsExpectedJsonArrays(finalJsonArray, expectedJsonArray); }