deepened unwrapping of json to include only value in list

This commit is contained in:
Adrian Cole 2011-02-06 17:29:15 -08:00
parent f1a878f016
commit 6f545c0277
5 changed files with 133 additions and 7 deletions

View File

@ -142,7 +142,7 @@ public class SimpleDateFormatDateService implements DateService {
public static final Pattern NANOS_TO_MILLIS_PATTERN = Pattern public static final Pattern NANOS_TO_MILLIS_PATTERN = Pattern
.compile(".*[0-9][0-9][0-9][0-9][0-9][0-9]"); .compile(".*[0-9][0-9][0-9][0-9][0-9][0-9]");
public static final Pattern TZ_PATTERN = Pattern.compile(".*[+-][0-9][0-9]:[0-9][0-9]"); public static final Pattern TZ_PATTERN = Pattern.compile(".*[+-][0-9][0-9]:?[0-9][0-9]");
private String trimNanosToMillis(String toParse) { private String trimNanosToMillis(String toParse) {
if (NANOS_TO_MILLIS_PATTERN.matcher(toParse).matches()) if (NANOS_TO_MILLIS_PATTERN.matcher(toParse).matches())

View File

@ -0,0 +1,52 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.http.functions;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.http.HttpResponse;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
/**
* @author Adrian Cole
*/
@Singleton
public class UnwrapOnlyNestedJsonValueInSet<T> implements Function<HttpResponse, T> {
private final UnwrapOnlyNestedJsonValue<Set<T>> json;
@Inject
UnwrapOnlyNestedJsonValueInSet(UnwrapOnlyNestedJsonValue<Set<T>> json) {
this.json = json;
}
@Override
public T apply(HttpResponse arg0) {
Set<T> set = json.apply(arg0);
if (set == null || set.size() == 0)
return null;
return Iterables.getOnlyElement(set);
}
}

View File

@ -24,9 +24,10 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.Map;
/** /**
* Unwraps the only value in the json reponse * Unwraps the only value in a nested json reponse
* *
* ex. { "foo" :"bar" } becomes "bar" * ex. { "foo" :"bar" } becomes "bar"
* *
@ -44,10 +45,27 @@ public @interface Unwrap {
* *
* ex. if (2) * ex. if (2)
* *
* { "foo" : {"bar" : "baz} } becomes "baz" * { "foo" : {"bar" : ["baz"]} } becomes ["baz"]
* *
* @return nestingLevel * @return nestingLevel
*/ */
int depth() default 1; int depth() default 1;
/**
* final collection type
*
* ex. if depth(2), edgeCollection(Map.class)
*
* { "foo" : {"bar" : ["baz"]} } becomes ["baz"]
*
* ex. if depth(3), edgeCollection(Set.class)
*
* { "foo" : {"bar" : ["baz"]} } becomes "baz"
*
* <h4>Note</h4> only Map and Set are valid
*
* @return
*/
Class<?> edgeCollection() default Map.class;
} }

View File

@ -88,6 +88,7 @@ import org.jclouds.http.functions.ReturnStringIf2xx;
import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet;
import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.options.HttpRequestOptions;
import org.jclouds.http.utils.ModifyRequest; import org.jclouds.http.utils.ModifyRequest;
@ -785,13 +786,17 @@ public class RestAnnotationProcessor<T> {
} }
ParameterizedType parserType; ParameterizedType parserType;
if (method.isAnnotationPresent(Unwrap.class)) { if (method.isAnnotationPresent(Unwrap.class)) {
int nestingLevel = method.getAnnotation(Unwrap.class).depth(); int depth = method.getAnnotation(Unwrap.class).depth();
if (nestingLevel == 1) Class edgeCollection = method.getAnnotation(Unwrap.class).edgeCollection();
if (depth == 1 && edgeCollection == Map.class)
parserType = Types.newParameterizedType(UnwrapOnlyJsonValue.class, returnVal); parserType = Types.newParameterizedType(UnwrapOnlyJsonValue.class, returnVal);
else if (nestingLevel == 2) else if (depth == 2 && edgeCollection == Map.class)
parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, returnVal); parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValue.class, returnVal);
else if (depth == 3 && edgeCollection == Set.class)
parserType = Types.newParameterizedType(UnwrapOnlyNestedJsonValueInSet.class, returnVal);
else else
throw new IllegalStateException("nesting level " + nestingLevel + " not yet supported for @Unwrap"); throw new IllegalStateException(String.format(
"depth(%d) edgeCollection(%s) not yet supported for @Unwrap", depth, edgeCollection));
} else { } else {
parserType = Types.newParameterizedType(ParseJson.class, returnVal); parserType = Types.newParameterizedType(ParseJson.class, returnVal);
} }

View File

@ -98,6 +98,7 @@ import org.jclouds.http.functions.ReturnStringIf2xx;
import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet;
import org.jclouds.http.internal.PayloadEnclosingImpl; import org.jclouds.http.internal.PayloadEnclosingImpl;
import org.jclouds.http.options.BaseHttpRequestOptions; import org.jclouds.http.options.BaseHttpRequestOptions;
import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.GetOptions;
@ -771,6 +772,12 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<? extends Set<String>> testUnwrapDepth2(); ListenableFuture<? extends Set<String>> testUnwrapDepth2();
@GET
@Path("/")
@Unwrap(depth = 3, edgeCollection = Set.class)
@Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<String> testUnwrapDepth3();
@Target( { ElementType.METHOD }) @Target( { ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@HttpMethod("ROWDY") @HttpMethod("ROWDY")
@ -955,6 +962,50 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
newStringPayload("{\"runit\":{\"runit\":[\"0.7.0\",\"0.7.1\"]}}"))), ImmutableSet.of("0.7.0", "0.7.1")); newStringPayload("{\"runit\":{\"runit\":[\"0.7.0\",\"0.7.1\"]}}"))), ImmutableSet.of("0.7.0", "0.7.1"));
} }
@SuppressWarnings("unchecked")
public void testUnwrapDepth3() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("testUnwrapDepth3");
HttpRequest request = factory(TestPut.class).createRequest(method);
assertResponseParserClassEquals(method, request, UnwrapOnlyNestedJsonValueInSet.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":{\"runit\":[\"0.7.0\"]}}"))),
"0.7.0");
}
@SuppressWarnings("unchecked")
public void testUnwrapDepth3None() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("testUnwrapDepth3");
HttpRequest request = factory(TestPut.class).createRequest(method);
assertResponseParserClassEquals(method, request, UnwrapOnlyNestedJsonValueInSet.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
.createResponseParser(parserFactory, injector, method, request);
assertEquals(parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":{\"runit\":[]}}"))), null);
}
@SuppressWarnings("unchecked")
@Test(expectedExceptions = IllegalArgumentException.class)
public void testUnwrapDepth3TooMany() throws SecurityException, NoSuchMethodException, IOException {
Method method = TestPut.class.getMethod("testUnwrapDepth3");
HttpRequest request = factory(TestPut.class).createRequest(method);
assertResponseParserClassEquals(method, request, UnwrapOnlyNestedJsonValueInSet.class);
// now test that it works!
Function<HttpResponse, Map<String, String>> parser = (Function<HttpResponse, Map<String, String>>) RestAnnotationProcessor
.createResponseParser(parserFactory, injector, method, request);
parser.apply(new HttpResponse(200, "ok", newStringPayload("{\"runit\":{\"runit\":[\"0.7.0\",\"0.7.1\"]}}")));
}
static class TestRequestFilter1 implements HttpRequestFilter { static class TestRequestFilter1 implements HttpRequestFilter {
public HttpRequest filter(HttpRequest request) throws HttpException { public HttpRequest filter(HttpRequest request) throws HttpException {
return request; return request;