From 55a10d4c5d6b4423df1663bc3b381b993987a1a0 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Wed, 30 Jan 2013 09:03:50 -0800 Subject: [PATCH] allow SelectJson to specify multiple field names, in case the server renamed something --- .../functions/ParseFirstJsonValueNamed.java | 28 ++++++++++--------- .../jclouds/rest/annotations/SelectJson.java | 5 ++-- .../ParseFirstJsonValueNamedTest.java | 13 +++++++++ 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/jclouds/http/functions/ParseFirstJsonValueNamed.java b/core/src/main/java/org/jclouds/http/functions/ParseFirstJsonValueNamed.java index 5fd5ddbf0a..753dadf347 100644 --- a/core/src/main/java/org/jclouds/http/functions/ParseFirstJsonValueNamed.java +++ b/core/src/main/java/org/jclouds/http/functions/ParseFirstJsonValueNamed.java @@ -53,12 +53,16 @@ public class ParseFirstJsonValueNamed implements Function { private final GsonWrapper json; private final TypeLiteral type; - private final String name; + private final ImmutableSet nameChoices; - public ParseFirstJsonValueNamed(GsonWrapper json, TypeLiteral type, String name) { + /** + * @param nameChoices + * tried in order, first match wins + */ + public ParseFirstJsonValueNamed(GsonWrapper json, TypeLiteral type, String... nameChoices) { this.json = checkNotNull(json, "json"); this.type = checkNotNull(type, "type"); - this.name = checkNotNull(name, "name"); + this.nameChoices = ImmutableSet.copyOf(checkNotNull(nameChoices, "nameChoices")); } @Override @@ -72,20 +76,19 @@ public class ParseFirstJsonValueNamed implements Function { reader.setLenient(true); AtomicReference name = Atomics.newReference(); JsonToken token = reader.peek(); - for (; token != JsonToken.END_DOCUMENT && nnn(this.name, reader, token, name); token = skipAndPeek(token, - reader)) { + for (; token != JsonToken.END_DOCUMENT && nnn(reader, token, name); token = skipAndPeek(token, reader)) { } 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(); - } else if (name.get().equals(this.name)) { + } else if (nameChoices.contains(name.get())) { return json.delegate(). fromJson(reader, type.getType()); } else { return nothing(); } } catch (IOException e) { 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 { Closeables.closeQuietly(reader); arg0.getPayload().release(); @@ -93,7 +96,7 @@ public class ParseFirstJsonValueNamed implements Function { } @SuppressWarnings("unchecked") - protected T nothing() { + private T nothing() { if (type.getRawType().isAssignableFrom(Set.class)) return (T) ImmutableSet.of(); else if (type.getRawType().isAssignableFrom(List.class)) @@ -103,11 +106,10 @@ public class ParseFirstJsonValueNamed implements Function { return null; } - protected boolean nnn(String toFind, JsonReader reader, JsonToken token, AtomicReference name) - throws IOException { + private boolean nnn(JsonReader reader, JsonToken token, AtomicReference name) throws IOException { if (token == JsonToken.NAME) { String name2 = reader.nextName(); - if (toFind.equals(name2)) { + if (nameChoices.contains(name2)) { name.set(name2); return false; } @@ -116,7 +118,7 @@ public class ParseFirstJsonValueNamed implements Function { } - public JsonToken skipAndPeek(JsonToken token, JsonReader reader) throws IOException { + private JsonToken skipAndPeek(JsonToken token, JsonReader reader) throws IOException { switch (token) { case BEGIN_ARRAY: reader.beginArray(); diff --git a/core/src/main/java/org/jclouds/rest/annotations/SelectJson.java b/core/src/main/java/org/jclouds/rest/annotations/SelectJson.java index e4cd5c80d3..35bbb04819 100644 --- a/core/src/main/java/org/jclouds/rest/annotations/SelectJson.java +++ b/core/src/main/java/org/jclouds/rest/annotations/SelectJson.java @@ -36,9 +36,8 @@ import java.lang.annotation.Target; public @interface SelectJson { /** - * - * @return + * Each of the keys are tried in order. This helps in the case the server renamed a field in json. */ - String value(); + String[] value(); } diff --git a/core/src/test/java/org/jclouds/http/functions/ParseFirstJsonValueNamedTest.java b/core/src/test/java/org/jclouds/http/functions/ParseFirstJsonValueNamedTest.java index 0f043bc8b9..8621bdf898 100644 --- a/core/src/test/java/org/jclouds/http/functions/ParseFirstJsonValueNamedTest.java +++ b/core/src/test/java/org/jclouds/http/functions/ParseFirstJsonValueNamedTest.java @@ -70,6 +70,19 @@ public class ParseFirstJsonValueNamedTest { }, "event").apply(response); 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 val = new ParseFirstJsonValueNamed>(json, new TypeLiteral>() { + }, "event", "_event").apply(response); + assertEquals(val.toString(), "[(name=GREETINGS, source=guest)]"); + } public void testParseNestedElementsWhenNotFoundIsEmpty() throws IOException { String nested = "{ \"count\":1 ,\"evant\" : [ {name:'GREETINGS',source:'guest'} ] }";