diff --git a/pom.xml b/pom.xml
index 40c5d423..f665bcd4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,13 +126,6 @@
objenesis${objenesis.version}
-
-
- javax.servlet
- servlet-api
- 2.5
- provided
- com.fasterxml.jackson.core
@@ -230,6 +223,22 @@
1.3test
+
+
+ net.jadler
+ jadler-all
+ 1.1.0
+ test
+
+
+
+
+
+ javax.servlet
+ servlet-api
+ 2.5
+ provided
+
diff --git a/readme.md b/readme.md
index 6c8b3c22..806cd0b9 100644
--- a/readme.md
+++ b/readme.md
@@ -263,3 +263,22 @@ When building links you usually need to determine the relation type to be used f
1. `@Controller` classes annotated with `@ExposesResourceFor` (see section on [EntityLinks](#entitylinks) for details) will transparently lookup the relation types for the type configured in the annotation, so that you can use `relProvider.getSingleResourceRelFor(MyController.class)` and get the relation type of the domain type exposed.
A `RelProvider` is exposed as Spring bean when using `@EnableHypermediaSupport` automatically. You can plug in custom providers by simply implementing the interface and exposing them as Spring bean in turn.
+
+## Traverson
+
+As of version 0.11 Spring HATEOAS provides an API for client side service traversal inspired by the [Traverson](https://blog.codecentric.de/en/2013/11/traverson/) JavaScript library.
+
+
+```java
+Map parameters = new HashMap<>();
+parameters.put("user", 27);
+
+Traverson traverson = new Traverson("http://localhost:8080/api/", MediaTypes.HAL_JSON);
+String name = traverson.follow("movies", "movie", "actor").
+ withTemplateParameters(parameters).
+ toObject("$.name");
+```
+
+You set up a `Traverson` instance by pointing it to a REST server and configure the media types you want to set as `Accept` header. You then go ahead and define the relation names you want to discover and follow. relation names can either be simple names or JSONPath expressions (starting with an `$`).
+
+The sample then hands a parameter map into the execution. The parameters will be used to expand URIs found during the traversal that are templated. The traversal is concluded by accessing the representation of the final traversal. In the case of the sample we evaluate a JSONPath expression to access the actor's name.
\ No newline at end of file
diff --git a/src/main/java/org/springframework/hateoas/client/Rels.java b/src/main/java/org/springframework/hateoas/client/Rels.java
new file mode 100644
index 00000000..df745a17
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/client/Rels.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2013-2014 the original author or authors.
+ *
+ * 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.springframework.hateoas.client;
+
+import org.springframework.hateoas.Link;
+import org.springframework.hateoas.LinkDiscoverer;
+import org.springframework.hateoas.LinkDiscoverers;
+import org.springframework.http.MediaType;
+import org.springframework.util.Assert;
+
+import com.jayway.jsonpath.JsonPath;
+
+/**
+ * Helper class to find {@link Link} instances in representations.
+ *
+ * @author Oliver Gierke
+ * @since 0.11
+ */
+class Rels {
+
+ /**
+ * Creates a new {@link Rel} for the given relation name and {@link LinkDiscoverers}.
+ *
+ * @param rel must not be {@literal null} or empty.
+ * @param discoverers must not be {@literal null}.
+ * @return
+ */
+ public static Rel getRelFor(String rel, LinkDiscoverers discoverers) {
+
+ Assert.hasText(rel, "Relation name must not be null!");
+ Assert.notNull(discoverers, "LinkDiscoverers must not be null!");
+
+ if (rel.startsWith("$")) {
+ return new JsonPathRel(rel);
+ }
+
+ return new LinkDiscovererRel(rel, discoverers);
+ }
+
+ public interface Rel {
+
+ /**
+ * Returns the link contained in the given representation of the given {@link MediaType}.
+ *
+ * @param representation
+ * @param mediaType
+ * @return
+ */
+ Link findInResponse(String representation, MediaType mediaType);
+ }
+
+ /**
+ * {@link Rel} to using a {@link LinkDiscoverer} based on the given {@link MediaType}.
+ *
+ * @author Oliver Gierke
+ */
+ private static class LinkDiscovererRel implements Rel {
+
+ private final String rel;
+ private final LinkDiscoverers discoverers;
+
+ /**
+ * Creates a new {@link LinkDiscovererRel} for the given relation name and {@link LinkDiscoverers}.
+ *
+ * @param rel must not be {@literal null} or empty.
+ * @param discoverers must not be {@literal null}.
+ */
+ private LinkDiscovererRel(String rel, LinkDiscoverers discoverers) {
+
+ Assert.hasText(rel, "Rel must not be null or empty!");
+ Assert.notNull(discoverers, "LinkDiscoverers must not be null!");
+
+ this.rel = rel;
+ this.discoverers = discoverers;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.hateoas.client.Rels.Rel#findInResponse(java.lang.String, org.springframework.http.MediaType)
+ */
+ @Override
+ public Link findInResponse(String response, MediaType mediaType) {
+ return discoverers.getLinkDiscovererFor(mediaType).findLinkWithRel(rel, response);
+ }
+ }
+
+ /**
+ * A relation that's being looked up by a JSONPath expression.
+ *
+ * @author Oliver Gierke
+ */
+ private static class JsonPathRel implements Rel {
+
+ private final String jsonPath;
+ private final String rel;
+
+ /**
+ * Creates a new {@link JsonPathRel} for the given JSON path.
+ *
+ * @param jsonPath must not be {@literal null} or empty.
+ */
+ private JsonPathRel(String jsonPath) {
+
+ Assert.hasText(jsonPath, "JSON path must not be null or empty!");
+
+ this.jsonPath = jsonPath;
+
+ String lastSegment = jsonPath.substring(jsonPath.lastIndexOf('.'));
+ this.rel = lastSegment.contains("[") ? lastSegment.substring(0, lastSegment.indexOf("[")) : lastSegment;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.hateoas.client.Rels.Rel#findInResponse(java.lang.String, org.springframework.http.MediaType)
+ */
+ @Override
+ public Link findInResponse(String representation, MediaType mediaType) {
+ return new Link(JsonPath.