diff --git a/src/main/java/org/json/HTTP.java b/src/main/java/org/json/HTTP.java
index 44ab3a6d3..85a435327 100644
--- a/src/main/java/org/json/HTTP.java
+++ b/src/main/java/org/json/HTTP.java
@@ -113,36 +113,66 @@ public static JSONObject toJSONObject(String string) throws JSONException {
public static String toString(JSONObject jo) throws JSONException {
StringBuilder sb = new StringBuilder();
if (jo.has("Status-Code") && jo.has("Reason-Phrase")) {
- sb.append(jo.getString("HTTP-Version"));
- sb.append(' ');
- sb.append(jo.getString("Status-Code"));
- sb.append(' ');
- sb.append(jo.getString("Reason-Phrase"));
+ appendResponseHeader(sb, jo);
} else if (jo.has("Method") && jo.has("Request-URI")) {
- sb.append(jo.getString("Method"));
- sb.append(' ');
- sb.append('"');
- sb.append(jo.getString("Request-URI"));
- sb.append('"');
- sb.append(' ');
- sb.append(jo.getString("HTTP-Version"));
+ appendRequestHeader(sb, jo);
} else {
throw new JSONException("Not enough material for an HTTP header.");
}
sb.append(CRLF);
- // Don't use the new entrySet API to maintain Android support
+
+ // Append other HTTP fields
+ appendAdditionalHeaders(sb, jo);
+
+ sb.append(CRLF);
+ return sb.toString();
+ }
+
+ /**
+ * Appends the HTTP response header to the StringBuilder.
+ * @param sb The StringBuilder to append to.
+ * @param jo The JSONObject representing the HTTP response.
+ * @throws JSONException if required fields are missing.
+ */
+ private static void appendResponseHeader(StringBuilder sb, JSONObject jo) throws JSONException {
+ sb.append(jo.getString("HTTP-Version"));
+ sb.append(' ');
+ sb.append(jo.getString("Status-Code"));
+ sb.append(' ');
+ sb.append(jo.getString("Reason-Phrase"));
+ }
+
+ /**
+ * Appends the HTTP request header to the StringBuilder.
+ * @param sb The StringBuilder to append to.
+ * @param jo The JSONObject representing the HTTP request.
+ * @throws JSONException if required fields are missing.
+ */
+ private static void appendRequestHeader(StringBuilder sb, JSONObject jo) throws JSONException {
+ sb.append(jo.getString("Method"));
+ sb.append(' ');
+ sb.append('"');
+ sb.append(jo.getString("Request-URI"));
+ sb.append('"');
+ sb.append(' ');
+ sb.append(jo.getString("HTTP-Version"));
+ }
+ /**
+ * Appends additional HTTP headers to the StringBuilder.
+ * @param sb The StringBuilder to append to.
+ * @param jo The JSONObject containing the additional headers.
+ */
+ private static void appendAdditionalHeaders(StringBuilder sb, JSONObject jo) {
for (final String key : jo.keySet()) {
String value = jo.optString(key);
- if (!"HTTP-Version".equals(key) && !"Status-Code".equals(key) &&
+ if (!"HTTP-Version".equals(key) && !"Status-Code".equals(key) &&
!"Reason-Phrase".equals(key) && !"Method".equals(key) &&
- !"Request-URI".equals(key) && !JSONObject.NULL.equals(value)) {
+ !"Request-URI".equals(key) && !JSONObject.NULL.equals(value)) {
sb.append(key);
sb.append(": ");
- sb.append(jo.optString(key));
+ sb.append(value);
sb.append(CRLF);
}
}
- sb.append(CRLF);
- return sb.toString();
}
}
diff --git a/src/main/java/org/json/HTTPTokener.java b/src/main/java/org/json/HTTPTokener.java
index 48cad31a3..b45b871ab 100644
--- a/src/main/java/org/json/HTTPTokener.java
+++ b/src/main/java/org/json/HTTPTokener.java
@@ -1,9 +1,5 @@
package org.json;
-/*
-Public Domain.
-*/
-
/**
* The HTTPTokener extends the JSONTokener to provide additional methods
* for the parsing of HTTP headers.
@@ -20,38 +16,27 @@ public HTTPTokener(String string) {
super(string);
}
-
/**
* Get the next token or string. This is used in parsing HTTP headers.
* @return A String.
* @throws JSONException if a syntax error occurs
*/
public String nextToken() throws JSONException {
+ char c = nextNonWhitespace();
+ TokenParser parser = TokenParsers.getParser(c);
+ return parser.parse(this, c);
+ }
+
+ /**
+ * Consume whitespace characters and return the first non-whitespace character.
+ * @return The first non-whitespace character.
+ * @throws JSONException if an error occurs while reading.
+ */
+ private char nextNonWhitespace() throws JSONException {
char c;
- char q;
- StringBuilder sb = new StringBuilder();
do {
c = next();
} while (Character.isWhitespace(c));
- if (c == '"' || c == '\'') {
- q = c;
- for (;;) {
- c = next();
- if (c < ' ') {
- throw syntaxError("Unterminated string.");
- }
- if (c == q) {
- return sb.toString();
- }
- sb.append(c);
- }
- }
- for (;;) {
- if (c == 0 || Character.isWhitespace(c)) {
- return sb.toString();
- }
- sb.append(c);
- c = next();
- }
+ return c;
}
}
diff --git a/src/main/java/org/json/JSONML.java b/src/main/java/org/json/JSONML.java
index 7b53e4da7..2d427726f 100644
--- a/src/main/java/org/json/JSONML.java
+++ b/src/main/java/org/json/JSONML.java
@@ -19,7 +19,7 @@ public class JSONML {
*/
public JSONML() {
}
-
+ public static final JSONMLParserConfiguration JSONML_PARSER_CONFIGURATION = new JSONMLParserConfiguration();
/**
* Parse XML values and store them in a JSONArray.
* @param x The XMLTokener containing the source string.
@@ -38,7 +38,7 @@ private static Object parse(
int currentNestingDepth
) throws JSONException {
return parse(x,arrayForm, ja,
- keepStrings ? JSONMLParserConfiguration.KEEP_STRINGS : JSONMLParserConfiguration.ORIGINAL,
+ keepStrings ? JSONMLParserConfiguration.KEEP_STRINGS : JSONML_PARSER_CONFIGURATION,
currentNestingDepth);
}
@@ -81,10 +81,10 @@ private static Object parse(
throw x.syntaxError("Bad XML");
}
token = x.nextContent();
- if (token == XML.LT) {
+ if (token == XMLSpecialCharacters.getLt()) {
token = x.nextToken();
if (token instanceof Character) {
- if (token == XML.SLASH) {
+ if (token == XMLStructuralCharacters.getSlash()) {
// Close tag
@@ -94,11 +94,11 @@ private static Object parse(
"Expected a closing name instead of '" +
token + "'.");
}
- if (x.nextToken() != XML.GT) {
+ if (x.nextToken() != XMLSpecialCharacters.getGt()) {
throw x.syntaxError("Misshaped close tag");
}
return token;
- } else if (token == XML.BANG) {
+ } else if (token == XMLSpecialCharacters.getBang()) {
// ' after ' 0);
}
- } else if (token == XML.QUEST) {
+ } else if (token == XMLStructuralCharacters.getQuest()) {
//
@@ -179,7 +179,7 @@ private static Object parse(
throw x.syntaxError("Reserved attribute.");
}
token = x.nextToken();
- if (token == XML.EQ) {
+ if (token == XMLSpecialCharacters.getEq()) {
token = x.nextToken();
if (!(token instanceof String)) {
throw x.syntaxError("Missing value");
@@ -196,8 +196,8 @@ private static Object parse(
// Empty tag <.../>
- if (token == XML.SLASH) {
- if (x.nextToken() != XML.GT) {
+ if (token == XMLStructuralCharacters.getSlash()) {
+ if (x.nextToken() != XMLSpecialCharacters.getGt()) {
throw x.syntaxError("Misshaped tag");
}
if (ja == null) {
@@ -210,7 +210,7 @@ private static Object parse(
// Content, between <...> and
} else {
- if (token != XML.GT) {
+ if (token != XMLSpecialCharacters.getGt()) {
throw x.syntaxError("Misshaped tag");
}
@@ -261,7 +261,7 @@ private static Object parse(
* @throws JSONException Thrown on error converting to a JSONArray
*/
public static JSONArray toJSONArray(String string) throws JSONException {
- return (JSONArray)parse(new XMLTokener(string), true, null, JSONMLParserConfiguration.ORIGINAL, 0);
+ return (JSONArray)parse(new XMLTokener(string), true, null, JSONML_PARSER_CONFIGURATION, 0);
}
diff --git a/src/main/java/org/json/JSONMLParserConfiguration.java b/src/main/java/org/json/JSONMLParserConfiguration.java
index 43ba0db62..aca9aaf40 100644
--- a/src/main/java/org/json/JSONMLParserConfiguration.java
+++ b/src/main/java/org/json/JSONMLParserConfiguration.java
@@ -14,9 +14,7 @@ public class JSONMLParserConfiguration extends ParserConfiguration {
*/
public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = ParserConfiguration.DEFAULT_MAXIMUM_NESTING_DEPTH;
- /** Original Configuration of the XML to JSONML Parser. */
- public static final JSONMLParserConfiguration ORIGINAL
- = new JSONMLParserConfiguration();
+
/** Original configuration of the XML to JSONML Parser except that values are kept as strings. */
public static final JSONMLParserConfiguration KEEP_STRINGS
= new JSONMLParserConfiguration().withKeepStrings(true);
diff --git a/src/main/java/org/json/JSONObject.java b/src/main/java/org/json/JSONObject.java
index 26a68c6dc..70b0976e8 100644
--- a/src/main/java/org/json/JSONObject.java
+++ b/src/main/java/org/json/JSONObject.java
@@ -201,7 +201,7 @@ public JSONObject(JSONTokener x) throws JSONException {
/**
* Construct a JSONObject from a JSONTokener with custom json parse configurations.
*
- * @param x
+ * @param jsonTokener
* A JSONTokener object containing the source string.
* @param jsonParserConfiguration
* Variable to pass parser custom configuration for json parsing.
@@ -209,30 +209,30 @@ public JSONObject(JSONTokener x) throws JSONException {
* If there is a syntax error in the source string or a
* duplicated key.
*/
- public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
+ public JSONObject(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration) throws JSONException {
this();
- char c;
+ char currentChar;
String key;
- if (x.nextClean() != '{') {
- throw x.syntaxError("A JSONObject text must begin with '{'");
+ if (jsonTokener.nextClean() != '{') {
+ throw jsonTokener.syntaxError("A JSONObject text must begin with '{'");
}
for (;;) {
- c = x.nextClean();
- switch (c) {
+ currentChar = jsonTokener.nextClean();
+ switch (currentChar) {
case 0:
- throw x.syntaxError("A JSONObject text must end with '}'");
+ throw jsonTokener.syntaxError("A JSONObject text must end with '}'");
case '}':
return;
default:
- key = x.nextSimpleValue(c).toString();
+ key = jsonTokener.nextSimpleValue(currentChar).toString();
}
// The key is followed by ':'.
- c = x.nextClean();
- if (c != ':') {
- throw x.syntaxError("Expected a ':' after a key");
+ currentChar = jsonTokener.nextClean();
+ if (currentChar != ':') {
+ throw jsonTokener.syntaxError("Expected a ':' after a key");
}
// Use syntaxError(..) to include error location
@@ -241,10 +241,10 @@ public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration
// Check if key exists
boolean keyExists = this.opt(key) != null;
if (keyExists && !jsonParserConfiguration.isOverwriteDuplicateKey()) {
- throw x.syntaxError("Duplicate key \"" + key + "\"");
+ throw jsonTokener.syntaxError("Duplicate key \"" + key + "\"");
}
- Object value = x.nextValue();
+ Object value = jsonTokener.nextValue();
// Only add value if non-null
if (value != null) {
this.put(key, value);
@@ -253,21 +253,21 @@ public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration
// Pairs are separated by ','.
- switch (x.nextClean()) {
+ switch (jsonTokener.nextClean()) {
case ';':
case ',':
- if (x.nextClean() == '}') {
+ if (jsonTokener.nextClean() == '}') {
return;
}
- if (x.end()) {
- throw x.syntaxError("A JSONObject text must end with '}'");
+ if (jsonTokener.end()) {
+ throw jsonTokener.syntaxError("A JSONObject text must end with '}'");
}
- x.back();
+ jsonTokener.back();
break;
case '}':
return;
default:
- throw x.syntaxError("Expected a ',' or '}'");
+ throw jsonTokener.syntaxError("Expected a ',' or '}'");
}
}
}
diff --git a/src/main/java/org/json/JSONWriter.java b/src/main/java/org/json/JSONWriter.java
index 11f4a5c7e..f5cf967ad 100644
--- a/src/main/java/org/json/JSONWriter.java
+++ b/src/main/java/org/json/JSONWriter.java
@@ -328,8 +328,7 @@ public static String valueToString(Object value) throws JSONException {
// Instead we will quote it as a string
return JSONObject.quote(numberAsString);
}
- if (value instanceof Boolean || value instanceof JSONObject
- || value instanceof JSONArray) {
+ if (isDirectlyConvertibleToJSON(value)) {
return value.toString();
}
if (value instanceof Map) {
@@ -349,6 +348,10 @@ public static String valueToString(Object value) throws JSONException {
return JSONObject.quote(value.toString());
}
+ private static boolean isDirectlyConvertibleToJSON(Object value) {
+ return value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray;
+ }
+
/**
* Append either the value true or the value
* false.
diff --git a/src/main/java/org/json/QuotedStringParser.java b/src/main/java/org/json/QuotedStringParser.java
new file mode 100644
index 000000000..a19b6ec52
--- /dev/null
+++ b/src/main/java/org/json/QuotedStringParser.java
@@ -0,0 +1,22 @@
+package org.json;
+
+/**
+ * Parser for quoted strings.
+ */
+public class QuotedStringParser implements TokenParser {
+ @Override
+ public String parse(JSONTokener tokener, char startChar) throws JSONException {
+ char q = startChar;
+ StringBuilder sb = new StringBuilder();
+ for (;;) {
+ char c = tokener.next();
+ if (c < ' ') {
+ throw tokener.syntaxError("Unterminated string.");
+ }
+ if (c == q) {
+ return sb.toString();
+ }
+ sb.append(c);
+ }
+ }
+}
diff --git a/src/main/java/org/json/TokenParser.java b/src/main/java/org/json/TokenParser.java
new file mode 100644
index 000000000..df7a39ec3
--- /dev/null
+++ b/src/main/java/org/json/TokenParser.java
@@ -0,0 +1,15 @@
+package org.json;
+
+/**
+ * Interface for token parsers.
+ */
+public interface TokenParser {
+ /**
+ * Parse the token from the given JSONTokener starting with the specified character.
+ * @param tokener The JSONTokener instance.
+ * @param startChar The starting character of the token.
+ * @return The parsed token as a string.
+ * @throws JSONException if a syntax error occurs.
+ */
+ String parse(JSONTokener tokener, char startChar) throws JSONException;
+}
diff --git a/src/main/java/org/json/TokenParsers.java b/src/main/java/org/json/TokenParsers.java
new file mode 100644
index 000000000..5a0e44e93
--- /dev/null
+++ b/src/main/java/org/json/TokenParsers.java
@@ -0,0 +1,15 @@
+package org.json;
+
+public class TokenParsers {
+ /**
+ * Get the appropriate parser for the starting character.
+ * @param c The starting character.
+ * @return A TokenParser instance.
+ */
+ public static TokenParser getParser(char c) {
+ if (c == '"' || c == '\'') {
+ return new QuotedStringParser();
+ }
+ return new UnquotedTokenParser();
+ }
+}
diff --git a/src/main/java/org/json/UnquotedTokenParser.java b/src/main/java/org/json/UnquotedTokenParser.java
new file mode 100644
index 000000000..bfdbd3928
--- /dev/null
+++ b/src/main/java/org/json/UnquotedTokenParser.java
@@ -0,0 +1,19 @@
+package org.json;
+
+/**
+ * Parser for unquoted tokens.
+ */
+public class UnquotedTokenParser implements TokenParser {
+ @Override
+ public String parse(JSONTokener tokener, char startChar) throws JSONException {
+ StringBuilder sb = new StringBuilder();
+ char c = startChar;
+ for (;;) {
+ if (c == 0 || Character.isWhitespace(c)) {
+ return sb.toString();
+ }
+ sb.append(c);
+ c = tokener.next();
+ }
+ }
+}
diff --git a/src/main/java/org/json/XML.java b/src/main/java/org/json/XML.java
index e59ec7a4a..a78f5d710 100644
--- a/src/main/java/org/json/XML.java
+++ b/src/main/java/org/json/XML.java
@@ -26,42 +26,6 @@ public class XML {
public XML() {
}
- /** The Character '&'. */
- public static final Character AMP = '&';
-
- /** The Character '''. */
- public static final Character APOS = '\'';
-
- /** The Character '!'. */
- public static final Character BANG = '!';
-
- /** The Character '='. */
- public static final Character EQ = '=';
-
- /** The Character
{@code '>'. }*/
- public static final Character GT = '>';
-
- /** The Character '<'. */
- public static final Character LT = '<';
-
- /** The Character '?'. */
- public static final Character QUEST = '?';
-
- /** The Character '"'. */
- public static final Character QUOT = '"';
-
- /** The Character '/'. */
- public static final Character SLASH = '/';
-
- /**
- * Null attribute name
- */
- public static final String NULL_ATTR = "xsi:nil";
-
- /**
- * Represents the XML attribute name for specifying type information.
- */
- public static final String TYPE_ATTR = "xsi:type";
/**
* Creates an iterator for navigating Code Points in a string instead of
@@ -268,7 +232,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
// ' after ' 0);
return false;
- } else if (token == QUEST) {
+ } else if (token == XMLStructuralCharacters.getQuest()) {
//
x.skipPast("?>");
return false;
- } else if (token == SLASH) {
+ } else if (token == XMLStructuralCharacters.getSlash()) {
// Close tag
@@ -317,7 +281,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
if (!token.equals(name)) {
throw x.syntaxError("Mismatched " + name + " and " + token);
}
- if (x.nextToken() != GT) {
+ if (x.nextToken() != XMLSpecialCharacters.getGt()) {
throw x.syntaxError("Misshaped close tag");
}
return true;
@@ -341,18 +305,18 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
if (token instanceof String) {
string = (String) token;
token = x.nextToken();
- if (token == EQ) {
+ if (token == XMLSpecialCharacters.getEq()) {
token = x.nextToken();
if (!(token instanceof String)) {
throw x.syntaxError("Missing value");
}
if (config.isConvertNilAttributeToNull()
- && NULL_ATTR.equals(string)
+ && XMLAttributeConstants.getNullAttr().equals(string)
&& Boolean.parseBoolean((String) token)) {
nilAttributeFound = true;
} else if(config.getXsiTypeMap() != null && !config.getXsiTypeMap().isEmpty()
- && TYPE_ATTR.equals(string)) {
+ && XMLAttributeConstants.getTypeAttr().equals(string)) {
xmlXsiTypeConverter = config.getXsiTypeMap().get(token);
} else if (!nilAttributeFound) {
jsonObject.accumulate(string,
@@ -366,9 +330,9 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
}
- } else if (token == SLASH) {
+ } else if (token == XMLStructuralCharacters.getSlash()) {
// Empty tag <.../>
- if (x.nextToken() != GT) {
+ if (x.nextToken() != XMLSpecialCharacters.getGt()) {
throw x.syntaxError("Misshaped tag");
}
if (config.getForceList().contains(tagName)) {
@@ -391,7 +355,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
}
return false;
- } else if (token == GT) {
+ } else if (token == XMLSpecialCharacters.getGt()) {
// Content, between <...> and
for (;;) {
token = x.nextContent();
@@ -412,7 +376,7 @@ private static boolean parse(XMLTokener x, JSONObject context, String name, XMLP
}
}
- } else if (token == LT) {
+ } else if (token == XMLSpecialCharacters.getLt()) {
// Nested element
if (currentNestingDepth == config.getMaxNestingDepth()) {
throw x.syntaxError("Maximum nesting depth of " + config.getMaxNestingDepth() + " reached");
diff --git a/src/main/java/org/json/XMLAttributeConstants.java b/src/main/java/org/json/XMLAttributeConstants.java
new file mode 100644
index 000000000..dc1cde65b
--- /dev/null
+++ b/src/main/java/org/json/XMLAttributeConstants.java
@@ -0,0 +1,17 @@
+package org.json;
+
+public class XMLAttributeConstants {
+ private static final String NULL_ATTR = "xsi:nil";
+ private static final String TYPE_ATTR = "xsi:type";
+
+ private XMLAttributeConstants() {
+ throw new UnsupportedOperationException("Utility class cannot be instantiated");
+ }
+ public static String getNullAttr() {
+ return NULL_ATTR;
+ }
+
+ public static String getTypeAttr() {
+ return TYPE_ATTR;
+ }
+}
diff --git a/src/main/java/org/json/XMLSpecialCharacters.java b/src/main/java/org/json/XMLSpecialCharacters.java
new file mode 100644
index 000000000..f6b90e52c
--- /dev/null
+++ b/src/main/java/org/json/XMLSpecialCharacters.java
@@ -0,0 +1,47 @@
+package org.json;
+/**
+ * A utility class containing XML special character constants.
+ */
+public class XMLSpecialCharacters {
+
+ /** The Character '&'. */
+ private static final Character AMP = '&';
+
+ /** The Character '<'. */
+ private static final Character LT = '<';
+
+ /** The Character {@code '>'. } */
+ private static final Character GT = '>';
+
+ /** The Character '='. */
+ private static final Character EQ = '=';
+
+ /** The Character '!'. */
+ private static final Character BANG = '!';
+
+ // Private constructor to prevent instantiation
+ private XMLSpecialCharacters() {
+ throw new UnsupportedOperationException("Utility class cannot be instantiated");
+ }
+
+ // Getter methods for constants
+ public static Character getAmp() {
+ return AMP;
+ }
+
+ public static Character getLt() {
+ return LT;
+ }
+
+ public static Character getGt() {
+ return GT;
+ }
+
+ public static Character getEq() {
+ return EQ;
+ }
+
+ public static Character getBang() {
+ return BANG;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/json/XMLStructuralCharacters.java b/src/main/java/org/json/XMLStructuralCharacters.java
new file mode 100644
index 000000000..5c380b077
--- /dev/null
+++ b/src/main/java/org/json/XMLStructuralCharacters.java
@@ -0,0 +1,41 @@
+package org.json;
+
+/**
+ * A utility class containing XML structural character constants.
+ */
+public class XMLStructuralCharacters {
+
+ /** The Character '/'. */
+ private static final Character SLASH = '/';
+
+ /** The Character '?'. */
+ private static final Character QUEST = '?';
+
+ /** The Character '''. */
+ private static final Character APOS = '\'';
+
+ /** The Character '"'. */
+ private static final Character QUOT = '"';
+
+ // Private constructor to prevent instantiation
+ private XMLStructuralCharacters() {
+ throw new UnsupportedOperationException("Utility class cannot be instantiated");
+ }
+
+ // Getter methods for constants
+ public static Character getSlash() {
+ return SLASH;
+ }
+
+ public static Character getQuest() {
+ return QUEST;
+ }
+
+ public static Character getApos() {
+ return APOS;
+ }
+
+ public static Character getQuot() {
+ return QUOT;
+ }
+}
diff --git a/src/main/java/org/json/XMLTokener.java b/src/main/java/org/json/XMLTokener.java
index bc18b31c9..d248190df 100644
--- a/src/main/java/org/json/XMLTokener.java
+++ b/src/main/java/org/json/XMLTokener.java
@@ -24,11 +24,11 @@ public class XMLTokener extends JSONTokener {
static {
entity = new java.util.HashMap