allow SelectJson to specify multiple field names, in case the server renamed something

This commit is contained in:
Adrian Cole 2013-01-30 09:03:50 -08:00
parent 5aa41d07aa
commit 55a10d4c5d
3 changed files with 30 additions and 16 deletions

View File

@ -53,12 +53,16 @@ public class ParseFirstJsonValueNamed<T> implements Function<HttpResponse, T> {
private final GsonWrapper json; private final GsonWrapper json;
private final TypeLiteral<T> type; private final TypeLiteral<T> type;
private final String name; private final ImmutableSet<String> nameChoices;
public ParseFirstJsonValueNamed(GsonWrapper json, TypeLiteral<T> type, String name) { /**
* @param nameChoices
* tried in order, first match wins
*/
public ParseFirstJsonValueNamed(GsonWrapper json, TypeLiteral<T> type, String... nameChoices) {
this.json = checkNotNull(json, "json"); this.json = checkNotNull(json, "json");
this.type = checkNotNull(type, "type"); this.type = checkNotNull(type, "type");
this.name = checkNotNull(name, "name"); this.nameChoices = ImmutableSet.copyOf(checkNotNull(nameChoices, "nameChoices"));
} }
@Override @Override
@ -72,20 +76,19 @@ public class ParseFirstJsonValueNamed<T> implements Function<HttpResponse, T> {
reader.setLenient(true); reader.setLenient(true);
AtomicReference<String> name = Atomics.newReference(); AtomicReference<String> name = Atomics.newReference();
JsonToken token = reader.peek(); JsonToken token = reader.peek();
for (; token != JsonToken.END_DOCUMENT && nnn(this.name, reader, token, name); token = skipAndPeek(token, for (; token != JsonToken.END_DOCUMENT && nnn(reader, token, name); token = skipAndPeek(token, reader)) {
reader)) {
} }
if (name.get() == null) { if (name.get() == null) {
logger.trace("did not object named %s in json from response %s", this.name, arg0); logger.trace("did not object named %s in json from response %s", nameChoices, arg0);
return nothing(); return nothing();
} else if (name.get().equals(this.name)) { } else if (nameChoices.contains(name.get())) {
return json.delegate().<T> fromJson(reader, type.getType()); return json.delegate().<T> fromJson(reader, type.getType());
} else { } else {
return nothing(); return nothing();
} }
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(String.format( throw new RuntimeException(String.format(
"error reading from stream, parsing object named %s from http response %s", this.name, arg0), e); "error reading from stream, parsing object named %s from http response %s", nameChoices, arg0), e);
} finally { } finally {
Closeables.closeQuietly(reader); Closeables.closeQuietly(reader);
arg0.getPayload().release(); arg0.getPayload().release();
@ -93,7 +96,7 @@ public class ParseFirstJsonValueNamed<T> implements Function<HttpResponse, T> {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected T nothing() { private T nothing() {
if (type.getRawType().isAssignableFrom(Set.class)) if (type.getRawType().isAssignableFrom(Set.class))
return (T) ImmutableSet.of(); return (T) ImmutableSet.of();
else if (type.getRawType().isAssignableFrom(List.class)) else if (type.getRawType().isAssignableFrom(List.class))
@ -103,11 +106,10 @@ public class ParseFirstJsonValueNamed<T> implements Function<HttpResponse, T> {
return null; return null;
} }
protected boolean nnn(String toFind, JsonReader reader, JsonToken token, AtomicReference<String> name) private boolean nnn(JsonReader reader, JsonToken token, AtomicReference<String> name) throws IOException {
throws IOException {
if (token == JsonToken.NAME) { if (token == JsonToken.NAME) {
String name2 = reader.nextName(); String name2 = reader.nextName();
if (toFind.equals(name2)) { if (nameChoices.contains(name2)) {
name.set(name2); name.set(name2);
return false; return false;
} }
@ -116,7 +118,7 @@ public class ParseFirstJsonValueNamed<T> implements Function<HttpResponse, T> {
} }
public JsonToken skipAndPeek(JsonToken token, JsonReader reader) throws IOException { private JsonToken skipAndPeek(JsonToken token, JsonReader reader) throws IOException {
switch (token) { switch (token) {
case BEGIN_ARRAY: case BEGIN_ARRAY:
reader.beginArray(); reader.beginArray();

View File

@ -36,9 +36,8 @@ import java.lang.annotation.Target;
public @interface SelectJson { public @interface SelectJson {
/** /**
* * Each of the keys are tried in order. This helps in the case the server renamed a field in json.
* @return
*/ */
String value(); String[] value();
} }

View File

@ -71,6 +71,19 @@ public class ParseFirstJsonValueNamedTest {
assertEquals(val.toString(), "[(name=GREETINGS, source=guest)]"); assertEquals(val.toString(), "[(name=GREETINGS, source=guest)]");
} }
/**
* scenario: server renames field from event to _event
*/
public void testParseRenamedField() throws IOException {
String nested = "{ \"count\":1 ,\"_event\" : [ {name:'GREETINGS',source:'guest'} ] }";
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie")
.payload(Payloads.newPayload(nested)).build();
List<Event> val = new ParseFirstJsonValueNamed<List<Event>>(json, new TypeLiteral<List<Event>>() {
}, "event", "_event").apply(response);
assertEquals(val.toString(), "[(name=GREETINGS, source=guest)]");
}
public void testParseNestedElementsWhenNotFoundIsEmpty() throws IOException { public void testParseNestedElementsWhenNotFoundIsEmpty() throws IOException {
String nested = "{ \"count\":1 ,\"evant\" : [ {name:'GREETINGS',source:'guest'} ] }"; String nested = "{ \"count\":1 ,\"evant\" : [ {name:'GREETINGS',source:'guest'} ] }";
HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie") HttpResponse response = HttpResponse.builder().statusCode(200).message("goodie")