
git-svn-id: svn://svn.code.sf.net/p/writer2latex/code/trunk@72 f0f2a975-2e09-46c8-9428-3b39399b9f3c
441 lines
No EOL
14 KiB
Java
441 lines
No EOL
14 KiB
Java
package org.json;
|
|
|
|
/*
|
|
Copyright (c) 2002 JSON.org
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
The Software shall be used for Good, not Evil.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
|
/**
|
|
* This provides static methods to convert an XML text into a JSONObject,
|
|
* and to covert a JSONObject into an XML text.
|
|
* @author JSON.org
|
|
* @version 2010-04-08
|
|
*/
|
|
public class XML {
|
|
|
|
/** The Character '&'. */
|
|
public static final Character AMP = new Character('&');
|
|
|
|
/** The Character '''. */
|
|
public static final Character APOS = new Character('\'');
|
|
|
|
/** The Character '!'. */
|
|
public static final Character BANG = new Character('!');
|
|
|
|
/** The Character '='. */
|
|
public static final Character EQ = new Character('=');
|
|
|
|
/** The Character '>'. */
|
|
public static final Character GT = new Character('>');
|
|
|
|
/** The Character '<'. */
|
|
public static final Character LT = new Character('<');
|
|
|
|
/** The Character '?'. */
|
|
public static final Character QUEST = new Character('?');
|
|
|
|
/** The Character '"'. */
|
|
public static final Character QUOT = new Character('"');
|
|
|
|
/** The Character '/'. */
|
|
public static final Character SLASH = new Character('/');
|
|
|
|
/**
|
|
* Replace special characters with XML escapes:
|
|
* <pre>
|
|
* & <small>(ampersand)</small> is replaced by &amp;
|
|
* < <small>(less than)</small> is replaced by &lt;
|
|
* > <small>(greater than)</small> is replaced by &gt;
|
|
* " <small>(double quote)</small> is replaced by &quot;
|
|
* </pre>
|
|
* @param string The string to be escaped.
|
|
* @return The escaped string.
|
|
*/
|
|
public static String escape(String string) {
|
|
StringBuffer sb = new StringBuffer();
|
|
for (int i = 0, len = string.length(); i < len; i++) {
|
|
char c = string.charAt(i);
|
|
switch (c) {
|
|
case '&':
|
|
sb.append("&");
|
|
break;
|
|
case '<':
|
|
sb.append("<");
|
|
break;
|
|
case '>':
|
|
sb.append(">");
|
|
break;
|
|
case '"':
|
|
sb.append(""");
|
|
break;
|
|
default:
|
|
sb.append(c);
|
|
}
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* Throw an exception if the string contains whitespace.
|
|
* Whitespace is not allowed in tagNames and attributes.
|
|
* @param string
|
|
* @throws JSONException
|
|
*/
|
|
public static void noSpace(String string) throws JSONException {
|
|
int i, length = string.length();
|
|
if (length == 0) {
|
|
throw new JSONException("Empty string.");
|
|
}
|
|
for (i = 0; i < length; i += 1) {
|
|
if (Character.isWhitespace(string.charAt(i))) {
|
|
throw new JSONException("'" + string +
|
|
"' contains a space character.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scan the content following the named tag, attaching it to the context.
|
|
* @param x The XMLTokener containing the source string.
|
|
* @param context The JSONObject that will include the new material.
|
|
* @param name The tag name.
|
|
* @return true if the close tag is processed.
|
|
* @throws JSONException
|
|
*/
|
|
private static boolean parse(XMLTokener x, JSONObject context,
|
|
String name) throws JSONException {
|
|
char c;
|
|
int i;
|
|
String n;
|
|
JSONObject o = null;
|
|
String s;
|
|
Object t;
|
|
|
|
// Test for and skip past these forms:
|
|
// <!-- ... -->
|
|
// <! ... >
|
|
// <![ ... ]]>
|
|
// <? ... ?>
|
|
// Report errors for these forms:
|
|
// <>
|
|
// <=
|
|
// <<
|
|
|
|
t = x.nextToken();
|
|
|
|
// <!
|
|
|
|
if (t == BANG) {
|
|
c = x.next();
|
|
if (c == '-') {
|
|
if (x.next() == '-') {
|
|
x.skipPast("-->");
|
|
return false;
|
|
}
|
|
x.back();
|
|
} else if (c == '[') {
|
|
t = x.nextToken();
|
|
if (t.equals("CDATA")) {
|
|
if (x.next() == '[') {
|
|
s = x.nextCDATA();
|
|
if (s.length() > 0) {
|
|
context.accumulate("content", s);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
throw x.syntaxError("Expected 'CDATA['");
|
|
}
|
|
i = 1;
|
|
do {
|
|
t = x.nextMeta();
|
|
if (t == null) {
|
|
throw x.syntaxError("Missing '>' after '<!'.");
|
|
} else if (t == LT) {
|
|
i += 1;
|
|
} else if (t == GT) {
|
|
i -= 1;
|
|
}
|
|
} while (i > 0);
|
|
return false;
|
|
} else if (t == QUEST) {
|
|
|
|
// <?
|
|
|
|
x.skipPast("?>");
|
|
return false;
|
|
} else if (t == SLASH) {
|
|
|
|
// Close tag </
|
|
|
|
t = x.nextToken();
|
|
if (name == null) {
|
|
throw x.syntaxError("Mismatched close tag" + t);
|
|
}
|
|
if (!t.equals(name)) {
|
|
throw x.syntaxError("Mismatched " + name + " and " + t);
|
|
}
|
|
if (x.nextToken() != GT) {
|
|
throw x.syntaxError("Misshaped close tag");
|
|
}
|
|
return true;
|
|
|
|
} else if (t instanceof Character) {
|
|
throw x.syntaxError("Misshaped tag");
|
|
|
|
// Open tag <
|
|
|
|
} else {
|
|
n = (String)t;
|
|
t = null;
|
|
o = new JSONObject();
|
|
for (;;) {
|
|
if (t == null) {
|
|
t = x.nextToken();
|
|
}
|
|
|
|
// attribute = value
|
|
|
|
if (t instanceof String) {
|
|
s = (String)t;
|
|
t = x.nextToken();
|
|
if (t == EQ) {
|
|
t = x.nextToken();
|
|
if (!(t instanceof String)) {
|
|
throw x.syntaxError("Missing value");
|
|
}
|
|
o.accumulate(s, JSONObject.stringToValue((String)t));
|
|
t = null;
|
|
} else {
|
|
o.accumulate(s, "");
|
|
}
|
|
|
|
// Empty tag <.../>
|
|
|
|
} else if (t == SLASH) {
|
|
if (x.nextToken() != GT) {
|
|
throw x.syntaxError("Misshaped tag");
|
|
}
|
|
if (o.length() > 0) {
|
|
context.accumulate(n, o);
|
|
} else {
|
|
context.accumulate(n, "");
|
|
}
|
|
return false;
|
|
|
|
// Content, between <...> and </...>
|
|
|
|
} else if (t == GT) {
|
|
for (;;) {
|
|
t = x.nextContent();
|
|
if (t == null) {
|
|
if (n != null) {
|
|
throw x.syntaxError("Unclosed tag " + n);
|
|
}
|
|
return false;
|
|
} else if (t instanceof String) {
|
|
s = (String)t;
|
|
if (s.length() > 0) {
|
|
o.accumulate("content", JSONObject.stringToValue(s));
|
|
}
|
|
|
|
// Nested element
|
|
|
|
} else if (t == LT) {
|
|
if (parse(x, o, n)) {
|
|
if (o.length() == 0) {
|
|
context.accumulate(n, "");
|
|
} else if (o.length() == 1 &&
|
|
o.opt("content") != null) {
|
|
context.accumulate(n, o.opt("content"));
|
|
} else {
|
|
context.accumulate(n, o);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
throw x.syntaxError("Misshaped tag");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert a well-formed (but not necessarily valid) XML string into a
|
|
* JSONObject. Some information may be lost in this transformation
|
|
* because JSON is a data format and XML is a document format. XML uses
|
|
* elements, attributes, and content text, while JSON uses unordered
|
|
* collections of name/value pairs and arrays of values. JSON does not
|
|
* does not like to distinguish between elements and attributes.
|
|
* Sequences of similar elements are represented as JSONArrays. Content
|
|
* text may be placed in a "content" member. Comments, prologs, DTDs, and
|
|
* <code><[ [ ]]></code> are ignored.
|
|
* @param string The source string.
|
|
* @return A JSONObject containing the structured data from the XML string.
|
|
* @throws JSONException
|
|
*/
|
|
public static JSONObject toJSONObject(String string) throws JSONException {
|
|
JSONObject o = new JSONObject();
|
|
XMLTokener x = new XMLTokener(string);
|
|
while (x.more() && x.skipPast("<")) {
|
|
parse(x, o, null);
|
|
}
|
|
return o;
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert a JSONObject into a well-formed, element-normal XML string.
|
|
* @param o A JSONObject.
|
|
* @return A string.
|
|
* @throws JSONException
|
|
*/
|
|
public static String toString(Object o) throws JSONException {
|
|
return toString(o, null);
|
|
}
|
|
|
|
|
|
/**
|
|
* Convert a JSONObject into a well-formed, element-normal XML string.
|
|
* @param o A JSONObject.
|
|
* @param tagName The optional name of the enclosing tag.
|
|
* @return A string.
|
|
* @throws JSONException
|
|
*/
|
|
public static String toString(Object o, String tagName)
|
|
throws JSONException {
|
|
StringBuffer b = new StringBuffer();
|
|
int i;
|
|
JSONArray ja;
|
|
JSONObject jo;
|
|
String k;
|
|
Iterator keys;
|
|
int len;
|
|
String s;
|
|
Object v;
|
|
if (o instanceof JSONObject) {
|
|
|
|
// Emit <tagName>
|
|
|
|
if (tagName != null) {
|
|
b.append('<');
|
|
b.append(tagName);
|
|
b.append('>');
|
|
}
|
|
|
|
// Loop thru the keys.
|
|
|
|
jo = (JSONObject)o;
|
|
keys = jo.keys();
|
|
while (keys.hasNext()) {
|
|
k = keys.next().toString();
|
|
v = jo.opt(k);
|
|
if (v == null) {
|
|
v = "";
|
|
}
|
|
if (v instanceof String) {
|
|
s = (String)v;
|
|
} else {
|
|
s = null;
|
|
}
|
|
|
|
// Emit content in body
|
|
|
|
if (k.equals("content")) {
|
|
if (v instanceof JSONArray) {
|
|
ja = (JSONArray)v;
|
|
len = ja.length();
|
|
for (i = 0; i < len; i += 1) {
|
|
if (i > 0) {
|
|
b.append('\n');
|
|
}
|
|
b.append(escape(ja.get(i).toString()));
|
|
}
|
|
} else {
|
|
b.append(escape(v.toString()));
|
|
}
|
|
|
|
// Emit an array of similar keys
|
|
|
|
} else if (v instanceof JSONArray) {
|
|
ja = (JSONArray)v;
|
|
len = ja.length();
|
|
for (i = 0; i < len; i += 1) {
|
|
v = ja.get(i);
|
|
if (v instanceof JSONArray) {
|
|
b.append('<');
|
|
b.append(k);
|
|
b.append('>');
|
|
b.append(toString(v));
|
|
b.append("</");
|
|
b.append(k);
|
|
b.append('>');
|
|
} else {
|
|
b.append(toString(v, k));
|
|
}
|
|
}
|
|
} else if (v.equals("")) {
|
|
b.append('<');
|
|
b.append(k);
|
|
b.append("/>");
|
|
|
|
// Emit a new tag <k>
|
|
|
|
} else {
|
|
b.append(toString(v, k));
|
|
}
|
|
}
|
|
if (tagName != null) {
|
|
|
|
// Emit the </tagname> close tag
|
|
|
|
b.append("</");
|
|
b.append(tagName);
|
|
b.append('>');
|
|
}
|
|
return b.toString();
|
|
|
|
// XML does not have good support for arrays. If an array appears in a place
|
|
// where XML is lacking, synthesize an <array> element.
|
|
|
|
} else if (o instanceof JSONArray) {
|
|
ja = (JSONArray)o;
|
|
len = ja.length();
|
|
for (i = 0; i < len; ++i) {
|
|
v = ja.opt(i);
|
|
b.append(toString(v, (tagName == null) ? "array" : tagName));
|
|
}
|
|
return b.toString();
|
|
} else {
|
|
s = (o == null) ? "null" : escape(o.toString());
|
|
return (tagName == null) ? "\"" + s + "\"" :
|
|
(s.length() == 0) ? "<" + tagName + "/>" :
|
|
"<" + tagName + ">" + s + "</" + tagName + ">";
|
|
}
|
|
}
|
|
} |