1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/ConvertTypesTest.java Tue Jul 22 06:30:19 2014 +0200
1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/ConvertTypesTest.java Thu Jul 24 16:20:47 2014 +0200
1.3 @@ -43,9 +43,12 @@
1.4 package net.java.html.json.tests;
1.5
1.6 import java.io.ByteArrayInputStream;
1.7 +import java.io.EOFException;
1.8 import java.io.InputStream;
1.9 import java.io.UnsupportedEncodingException;
1.10 +import java.util.ArrayList;
1.11 import java.util.HashMap;
1.12 +import java.util.List;
1.13 import java.util.Map;
1.14 import net.java.html.BrwsrCtx;
1.15 import net.java.html.json.Models;
1.16 @@ -56,18 +59,33 @@
1.17 * @author Jaroslav Tulach <jtulach@netbeans.org>
1.18 */
1.19 public final class ConvertTypesTest {
1.20 - private static InputStream createIS(boolean includeSex, boolean includeAddress)
1.21 + private static InputStream createIS(boolean includeSex, boolean includeAddress, int array)
1.22 throws UnsupportedEncodingException {
1.23 StringBuilder sb = new StringBuilder();
1.24 - sb.append("{ \"firstName\" : \"son\",\n");
1.25 - sb.append(" \"lastName\" : \"dj\" \n");
1.26 - if (includeSex) {
1.27 - sb.append(", \"sex\" : \"MALE\" \n");
1.28 + int repeat;
1.29 + if (array != -1) {
1.30 + sb.append("[\n");
1.31 + repeat = array;
1.32 + } else {
1.33 + repeat = 1;
1.34 }
1.35 - if (includeAddress) {
1.36 - sb.append(", \"address\" : { \"street\" : \"Schnirchova\" } \n");
1.37 + for (int i = 0; i < repeat; i++) {
1.38 + sb.append("{ \"firstName\" : \"son\",\n");
1.39 + sb.append(" \"lastName\" : \"dj\" \n");
1.40 + if (includeSex) {
1.41 + sb.append(", \"sex\" : \"MALE\" \n");
1.42 + }
1.43 + if (includeAddress) {
1.44 + sb.append(", \"address\" : { \"street\" : \"Schnirchova\" } \n");
1.45 + }
1.46 + sb.append("}\n");
1.47 + if (i < array - 1) {
1.48 + sb.append(",");
1.49 + }
1.50 }
1.51 - sb.append("}\n");
1.52 + if (array != -1) {
1.53 + sb.append(']');
1.54 + }
1.55 return new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
1.56 }
1.57 private static Object createJSON(boolean includeSex)
1.58 @@ -95,7 +113,7 @@
1.59 @KOTest
1.60 public void parseConvertToPeople() throws Exception {
1.61 final BrwsrCtx c = newContext();
1.62 - final InputStream o = createIS(true, false);
1.63 + final InputStream o = createIS(true, false, -1);
1.64
1.65 Person p = Models.parse(c, Person.class, o);
1.66
1.67 @@ -107,7 +125,7 @@
1.68 @KOTest
1.69 public void parseConvertToPeopleWithAddress() throws Exception {
1.70 final BrwsrCtx c = newContext();
1.71 - final InputStream o = createIS(true, true);
1.72 + final InputStream o = createIS(true, true, -1);
1.73
1.74 Person p = Models.parse(c, Person.class, o);
1.75
1.76 @@ -119,6 +137,59 @@
1.77 }
1.78
1.79 @KOTest
1.80 + public void parseConvertToPeopleWithAddressIntoAnArray() throws Exception {
1.81 + final BrwsrCtx c = newContext();
1.82 + final InputStream o = createIS(true, true, -1);
1.83 +
1.84 + List<Person> arr = new ArrayList<Person>();
1.85 + Models.parse(c, Person.class, o, arr);
1.86 +
1.87 + assert arr.size() == 1 : "There is one item in " + arr;
1.88 +
1.89 + Person p = arr.get(0);
1.90 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
1.91 + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
1.92 + assert Sex.MALE.equals(p.getSex()) : "Sex: " + p.getSex();
1.93 + assert p.getAddress() != null : "Some address provided";
1.94 + assert p.getAddress().getStreet().equals("Schnirchova") : "Is Schnirchova: " + p.getAddress();
1.95 + }
1.96 +
1.97 + @KOTest
1.98 + public void parseNullValue() throws Exception {
1.99 + final BrwsrCtx c = newContext();
1.100 +
1.101 + StringBuilder sb = new StringBuilder();
1.102 + sb.append("{ \"firstName\" : \"son\",\n");
1.103 + sb.append(" \"lastName\" : null } \n");
1.104 +
1.105 + final ByteArrayInputStream is = new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
1.106 + Person p = Models.parse(c, Person.class, is);
1.107 +
1.108 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
1.109 + assert null == p.getLastName() : "Last name: " + p.getLastName();
1.110 + }
1.111 +
1.112 + @KOTest
1.113 + public void parseNullArrayValue() throws Exception {
1.114 + final BrwsrCtx c = newContext();
1.115 +
1.116 + StringBuilder sb = new StringBuilder();
1.117 + sb.append("[ null, { \"firstName\" : \"son\",\n");
1.118 + sb.append(" \"lastName\" : null } ]\n");
1.119 +
1.120 + final ByteArrayInputStream is = new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
1.121 + List<Person> arr = new ArrayList<Person>();
1.122 + Models.parse(c, Person.class, is, arr);
1.123 +
1.124 + assert arr.size() == 2 : "There are two items in " + arr;
1.125 + assert arr.get(0) == null : "first is null " + arr;
1.126 +
1.127 + Person p = arr.get(1);
1.128 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
1.129 + assert null == p.getLastName() : "Last name: " + p.getLastName();
1.130 + }
1.131 +
1.132 + @KOTest
1.133 public void testConvertToPeopleWithoutSex() throws Exception {
1.134 final Object o = createJSON(false);
1.135
1.136 @@ -132,7 +203,7 @@
1.137 @KOTest
1.138 public void parseConvertToPeopleWithoutSex() throws Exception {
1.139 final BrwsrCtx c = newContext();
1.140 - final InputStream o = createIS(false, false);
1.141 + final InputStream o = createIS(false, false, -1);
1.142 Person p = Models.parse(c, Person.class, o);
1.143
1.144 assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
1.145 @@ -140,6 +211,73 @@
1.146 assert p.getSex() == null : "No sex: " + p.getSex();
1.147 }
1.148
1.149 + @KOTest
1.150 + public void parseConvertToPeopleWithAddressOnArray() throws Exception {
1.151 + final BrwsrCtx c = newContext();
1.152 + final InputStream o = createIS(true, true, 1);
1.153 +
1.154 + Person p = Models.parse(c, Person.class, o);
1.155 +
1.156 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
1.157 + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
1.158 + assert Sex.MALE.equals(p.getSex()) : "Sex: " + p.getSex();
1.159 + assert p.getAddress() != null : "Some address provided";
1.160 + assert p.getAddress().getStreet().equals("Schnirchova") : "Is Schnirchova: " + p.getAddress();
1.161 + }
1.162 +
1.163 + @KOTest
1.164 + public void parseConvertToPeopleWithoutSexOnArray() throws Exception {
1.165 + final BrwsrCtx c = newContext();
1.166 + final InputStream o = createIS(false, false, 1);
1.167 + Person p = Models.parse(c, Person.class, o);
1.168 +
1.169 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
1.170 + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
1.171 + assert p.getSex() == null : "No sex: " + p.getSex();
1.172 + }
1.173 +
1.174 + @KOTest
1.175 + public void parseFirstElementFromAbiggerArray() throws Exception {
1.176 + final BrwsrCtx c = newContext();
1.177 + final InputStream o = createIS(false, false, 5);
1.178 + Person p = Models.parse(c, Person.class, o);
1.179 +
1.180 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
1.181 + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
1.182 + assert p.getSex() == null : "No sex: " + p.getSex();
1.183 + }
1.184 +
1.185 + @KOTest
1.186 + public void parseAllElementFromAbiggerArray() throws Exception {
1.187 + final BrwsrCtx c = newContext();
1.188 + final InputStream o = createIS(false, false, 5);
1.189 +
1.190 + List<Person> res = new ArrayList<Person>();
1.191 + Models.parse(c, Person.class, o, res);
1.192 +
1.193 + assert res.size() == 5 : "Five elements found" + res;
1.194 +
1.195 + for (Person p : res) {
1.196 + assert "son".equals(p.getFirstName()) : "First name: " + p.getFirstName();
1.197 + assert "dj".equals(p.getLastName()) : "Last name: " + p.getLastName();
1.198 + assert p.getSex() == null : "No sex: " + p.getSex();
1.199 + }
1.200 + }
1.201 +
1.202 + @KOTest
1.203 + public void parseOnEmptyArray() throws Exception {
1.204 + final BrwsrCtx c = newContext();
1.205 + final InputStream o = createIS(false, false, 0);
1.206 +
1.207 + try {
1.208 + Models.parse(c, Person.class, o);
1.209 + } catch (EOFException ex) {
1.210 + // OK
1.211 + return;
1.212 + }
1.213 + throw new IllegalStateException("Should throw end of file exception, as the array is empty");
1.214 + }
1.215 +
1.216 private static BrwsrCtx newContext() {
1.217 return Utils.newContext(ConvertTypesTest.class);
1.218 }
2.1 --- a/json/src/main/java/net/java/html/json/Models.java Tue Jul 22 06:30:19 2014 +0200
2.2 +++ b/json/src/main/java/net/java/html/json/Models.java Thu Jul 24 16:20:47 2014 +0200
2.3 @@ -45,6 +45,7 @@
2.4 import net.java.html.BrwsrCtx;
2.5 import java.io.IOException;
2.6 import java.io.InputStream;
2.7 +import java.util.Collection;
2.8 import org.netbeans.html.json.impl.JSON;
2.9
2.10 /** Information about and
2.11 @@ -90,7 +91,29 @@
2.12 * @since 0.2
2.13 */
2.14 public static <M> M parse(BrwsrCtx c, Class<M> model, InputStream is) throws IOException {
2.15 - return JSON.readStream(c, model, is);
2.16 + return JSON.readStream(c, model, is, null);
2.17 + }
2.18 +
2.19 + /** Generic method to parse stream, that can possibly contain array
2.20 + * of specified objects.
2.21 + *
2.22 + * @param <M> the type of the individal JSON object
2.23 + * @param c context of the technology to use for reading
2.24 + * @param model the model class generated by {@link Model} annotation
2.25 + * @param is input stream with data
2.26 + * @param collectTo collection to add the individual model instances to.
2.27 + * If the stream contains an object, one instance will be added, if
2.28 + * it contains an array, the number of array items will be added to
2.29 + * the collection
2.30 + * @throws IOException thrown when an I/O problem appears
2.31 + * @since 0.8.3
2.32 + */
2.33 + public static <M> void parse(
2.34 + BrwsrCtx c, Class<M> model,
2.35 + InputStream is, Collection<? super M> collectTo
2.36 + ) throws IOException {
2.37 + collectTo.getClass();
2.38 + JSON.readStream(c, model, is, collectTo);
2.39 }
2.40
2.41 /** Converts an existing, raw, JSON object into a {@link Model model class}.
3.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Transfer.java Tue Jul 22 06:30:19 2014 +0200
3.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Transfer.java Thu Jul 24 16:20:47 2014 +0200
3.3 @@ -68,7 +68,10 @@
3.4 /** Reads content of a stream and creates its JSON representation.
3.5 * The returned object is implementation dependant. It however needs
3.6 * to be acceptable as first argument of {@link #extract(java.lang.Object, java.lang.String[], java.lang.Object[]) extract}
3.7 - * method.
3.8 + * method. If the stream contains representation or a JSON array,
3.9 + * an Object[] should be returned - each of its members should, by itself
3.10 + * be acceptable argument to
3.11 + * the {@link #extract(java.lang.Object, java.lang.String[], java.lang.Object[]) extract} method.
3.12 *
3.13 * @param is input stream to read data from
3.14 * @return an object representing the JSON data
4.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java Tue Jul 22 06:30:19 2014 +0200
4.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java Thu Jul 24 16:20:47 2014 +0200
4.3 @@ -42,6 +42,7 @@
4.4 */
4.5 package org.netbeans.html.json.impl;
4.6
4.7 +import java.io.EOFException;
4.8 import java.io.IOException;
4.9 import java.io.InputStream;
4.10 import java.util.Collection;
4.11 @@ -404,10 +405,28 @@
4.12 return PropertyBindingAccessor.clone(from, model, c);
4.13 }
4.14
4.15 - public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data)
4.16 + public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data, Collection<? super T> collectTo)
4.17 throws IOException {
4.18 Transfer tr = findTransfer(c);
4.19 - return read(c, modelClazz, tr.toJSON((InputStream)data));
4.20 + Object rawJSON = tr.toJSON((InputStream)data);
4.21 + if (rawJSON instanceof Object[]) {
4.22 + final Object[] arr = (Object[])rawJSON;
4.23 + if (collectTo != null) {
4.24 + for (int i = 0; i < arr.length; i++) {
4.25 + collectTo.add(read(c, modelClazz, arr[i]));
4.26 + }
4.27 + return null;
4.28 + }
4.29 + if (arr.length == 0) {
4.30 + throw new EOFException("Recieved an empty array");
4.31 + }
4.32 + rawJSON = arr[0];
4.33 + }
4.34 + T res = read(c, modelClazz, rawJSON);
4.35 + if (collectTo != null) {
4.36 + collectTo.add(res);
4.37 + }
4.38 + return res;
4.39 }
4.40 public static <T> T read(BrwsrCtx c, Class<T> modelClazz, Object data) {
4.41 if (data == null) {
5.1 --- a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java Tue Jul 22 06:30:19 2014 +0200
5.2 +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java Thu Jul 24 16:20:47 2014 +0200
5.3 @@ -123,43 +123,15 @@
5.4 final PushbackInputStream is = new PushbackInputStream(
5.5 conn.getInputStream(), 1
5.6 );
5.7 - boolean array = false;
5.8 - boolean string = false;
5.9 - if (call.isJSONP()) {
5.10 - for (;;) {
5.11 - int ch = is.read();
5.12 - if (ch == -1) {
5.13 - break;
5.14 - }
5.15 - if (ch == '[') {
5.16 - is.unread(ch);
5.17 - array = true;
5.18 - break;
5.19 - }
5.20 - if (ch == '{') {
5.21 - is.unread(ch);
5.22 - break;
5.23 - }
5.24 - }
5.25 - } else {
5.26 - int ch = is.read();
5.27 - if (ch == -1) {
5.28 - string = true;
5.29 - } else {
5.30 - array = ch == '[';
5.31 - is.unread(ch);
5.32 - if (!array && ch != '{') {
5.33 - string = true;
5.34 - }
5.35 - }
5.36 - }
5.37 + boolean[] arrayOrString = { false, false };
5.38 + detectJSONType(call.isJSONP(), is, arrayOrString);
5.39 try {
5.40 - if (string) {
5.41 + if (arrayOrString[1]) {
5.42 throw new JSONException("");
5.43 }
5.44 JSONTokener tok = createTokener(is);
5.45 Object obj;
5.46 - obj = array ? new JSONArray(tok) : new JSONObject(tok);
5.47 + obj = arrayOrString[0] ? new JSONArray(tok) : new JSONObject(tok);
5.48 json = convertToArray(obj);
5.49 } catch (JSONException ex) {
5.50 Reader r = new InputStreamReader(is, "UTF-8");
5.51 @@ -184,6 +156,34 @@
5.52 }
5.53 }
5.54
5.55 + private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException {
5.56 + for (;;) {
5.57 + int ch = is.read();
5.58 + if (ch == -1) {
5.59 + arrayOrString[1] = true;
5.60 + break;
5.61 + }
5.62 + if (Character.isWhitespace(ch)) {
5.63 + continue;
5.64 + }
5.65 +
5.66 + if (ch == '[') {
5.67 + is.unread(ch);
5.68 + arrayOrString[0] = true;
5.69 + break;
5.70 + }
5.71 + if (ch == '{') {
5.72 + is.unread(ch);
5.73 + break;
5.74 + }
5.75 + if (!skipAnything) {
5.76 + is.unread(ch);
5.77 + arrayOrString[1] = true;
5.78 + break;
5.79 + }
5.80 + }
5.81 + }
5.82 +
5.83 private static JSONTokener createTokener(InputStream is) throws IOException {
5.84 Reader r = new InputStreamReader(is, "UTF-8");
5.85 try {
5.86 @@ -218,6 +218,8 @@
5.87 obj.put(key, convertToArray(obj.get(key)));
5.88 }
5.89 return obj;
5.90 + } else if (o == JSONObject.NULL) {
5.91 + return null;
5.92 } else {
5.93 return o;
5.94 }
5.95 @@ -227,11 +229,11 @@
5.96 if (jsonObject instanceof JSONObject) {
5.97 JSONObject obj = (JSONObject)jsonObject;
5.98 for (int i = 0; i < props.length; i++) {
5.99 - try {
5.100 - values[i] = obj.has(props[i]) ? obj.get(props[i]) : null;
5.101 - } catch (JSONException ex) {
5.102 - LoadJSON.LOG.log(Level.SEVERE, "Can't read " + props[i] + " from " + jsonObject, ex);
5.103 + Object val = obj.opt(props[i]);
5.104 + if (val == JSONObject.NULL) {
5.105 + val = null;
5.106 }
5.107 + values[i] = val;
5.108 }
5.109 return;
5.110 }
5.111 @@ -252,8 +254,12 @@
5.112
5.113 public static Object parse(InputStream is) throws IOException {
5.114 try {
5.115 - JSONTokener t = createTokener(is);
5.116 - return new JSONObject(t);
5.117 + PushbackInputStream push = new PushbackInputStream(is, 1);
5.118 + boolean[] arrayOrString = { false, false };
5.119 + detectJSONType(false, push, arrayOrString);
5.120 + JSONTokener t = createTokener(push);
5.121 + Object obj = arrayOrString[0] ? new JSONArray(t) : new JSONObject(t);
5.122 + return convertToArray(obj);
5.123 } catch (JSONException ex) {
5.124 throw new IOException(ex);
5.125 }
6.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java Tue Jul 22 06:30:19 2014 +0200
6.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java Thu Jul 24 16:20:47 2014 +0200
6.3 @@ -166,10 +166,11 @@
6.4 + " if (request.readyState !== 4) return;\n"
6.5 + " var r = request.response || request.responseText;\n"
6.6 + " try {\n"
6.7 + + " if (request.status >= 400) throw request.status + ': ' + request.statusText;"
6.8 + " try { r = eval('(' + r + ')'); } catch (ignore) { }"
6.9 + " done.@org.apidesign.html.json.spi.JSONCall::notifySuccess(Ljava/lang/Object;)(r);\n"
6.10 + " } catch (error) {;\n"
6.11 - + " @org.netbeans.html.ko4j.LoadJSON::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, r);\n"
6.12 + + " @org.netbeans.html.ko4j.LoadJSON::notifyError(Ljava/lang/Object;Ljava/lang/Object;)(done, error);\n"
6.13 + " }\n"
6.14 + "};\n"
6.15 + "request.onerror = function (e) {\n"
7.1 --- a/src/main/javadoc/overview.html Tue Jul 22 06:30:19 2014 +0200
7.2 +++ b/src/main/javadoc/overview.html Thu Jul 24 16:20:47 2014 +0200
7.3 @@ -80,7 +80,10 @@
7.4 <p>
7.5 Setters or array properties on classes generated by {@link net.java.html.json.Model}
7.6 annotation can be accessed from any thread. {@link org.apidesign.html.sound.spi.AudioEnvironment}
7.7 - can be registered into {@link net.java.html.BrwsrCtx}.
7.8 + can be registered into {@link net.java.html.BrwsrCtx}. There is
7.9 + a {@link net.java.html.json.Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream, java.util.Collection) method}
7.10 + to parse a JSON array and convert it into
7.11 + {@link net.java.html.json.Model model classes}.
7.12 </p>
7.13
7.14 <h3>What's New in Version 0.8.2?</h3>