/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.json;
import static com.google.common.base.Objects.equal;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Atomics;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonParser;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.google.inject.TypeLiteral;
/**
*
* @author Adrian Cole
*/
@Test
public class GsonExperimentsTest {
public static final String json = "['hello',5,{name:'GREETINGS',source:'guest'}]";
static class Event {
private String name;
private String source;
private Event(String name, String source) {
this.name = name;
this.source = source;
}
@Override
public String toString() {
return String.format("(name=%s, source=%s)", name, source);
}
}
private Gson gson;
private String json2;
@BeforeTest
void setupSource() {
gson = new Gson();
Collection<Object> collection = Lists.newArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));
json2 = gson.toJson(collection);
assertEquals(json2, "[\"hello\",5,{\"name\":\"GREETINGS\",\"source\":\"guest\"}]");
}
public class OptionalTypeAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (typeToken.getRawType() != Optional.class || !(type instanceof ParameterizedType)) {
return null;
}
Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
return (TypeAdapter<T>) newOptionalAdapter(elementAdapter);
}
private <E> TypeAdapter<Optional<E>> newOptionalAdapter(final TypeAdapter<E> elementAdapter) {
return new TypeAdapter<Optional<E>>() {
public void write(JsonWriter out, Optional<E> value) throws IOException {
if (value == null || !value.isPresent()) {
out.nullValue();
return;
}
elementAdapter.write(out, value.get());
}
public Optional<E> read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return Optional.absent();
}
return Optional.of(elementAdapter.read(in));
}
};
}
}
static class OptionalType {
Optional<String> present = Optional.of("hello");
Optional<String> notPresent = Optional.absent();
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof OptionalType) {
final OptionalType other = OptionalType.class.cast(object);
return equal(present, other.present) && equal(notPresent, other.notPresent);
} else {
return false;
}
}
}
public void testPersistOptional() {
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new OptionalTypeAdapterFactory()).create();
String json = gson.toJson(new OptionalType());
assertEquals(json, "{\"present\":\"hello\"}");
assertEquals(gson.fromJson(json, OptionalType.class), new OptionalType());
}
// inspired by
// http://code.google.com/p/google-gson/source/browse/trunk/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java
public void testRawCollectionsWithParser() {
JsonParser parser = new JsonParser();
JsonArray array = parser.parse(json).getAsJsonArray();
String message = gson.fromJson(array.get(0), String.class);
int number = gson.fromJson(array.get(1), int.class);
Event event = gson.fromJson(array.get(2), Event.class);
assertEquals(message, "hello");
assertEquals(number, 5);
assertEquals(event.toString(), new Event("GREETINGS", "guest").toString());
}
private final String nested = "{ \"count\":1 ,\"event\" : [ {name:'GREETINGS',source:'guest'} ] }";
private final String nestedFurther = "{ \"listaccountsresponse\" : { \"count\":1 ,\"event\" : [ {name:'GREETINGS',source:'guest'} ] } }";
// inspired by http://sites.google.com/site/gson/streaming
public void testParseNestedElements() throws IOException {
JsonReader reader = new JsonReader(new StringReader(nested));
List<Event> val = parseThingFromReaderOrNull("event", reader, new TypeLiteral<List<Event>>() {
}.getType());
assertEquals(val.toString(), "[(name=GREETINGS, source=guest)]");
}
public void testParseNestedFurtherElements() throws IOException {
JsonReader reader = new JsonReader(new StringReader(nestedFurther));
List<Event> val = parseThingFromReaderOrNull("event", reader, new TypeLiteral<List<Event>>() {
}.getType());
assertEquals(val.toString(), "[(name=GREETINGS, source=guest)]");
}
protected <T> T parseThingFromReaderOrNull(String toFind, JsonReader reader, Type type) throws IOException {
AtomicReference<String> name = Atomics.newReference();
JsonToken token = reader.peek();
for (; token != JsonToken.END_DOCUMENT && nnn(toFind, reader, token, name); token = skipAndPeek(token, reader))
;
T val = gson.<T> fromJson(reader, type);
reader.close();
return val;
}
protected boolean nnn(String toFind, JsonReader reader, JsonToken token, AtomicReference<String> name)
throws IOException {
if (token == JsonToken.NAME) {
String name2 = reader.nextName();
if (toFind.equals(name2)) {
name.set(name2);
return false;
}
}
return true;
}
public JsonToken skipAndPeek(JsonToken token, JsonReader reader) throws IOException {
switch (token) {
case BEGIN_ARRAY:
reader.beginArray();
break;
case END_ARRAY:
reader.endArray();
break;
case BEGIN_OBJECT:
reader.beginObject();
break;
case END_OBJECT:
reader.endObject();
break;
case NAME:
// NOTE that we have already advanced on NAME in the eval block;
break;
case STRING:
reader.nextString();
break;
case NUMBER:
reader.nextString();
break;
case BOOLEAN:
reader.nextBoolean();
break;
case NULL:
reader.nextNull();
break;
case END_DOCUMENT:
break;
}
return reader.peek();
}
}