diff --git a/pom.xml b/pom.xml
index e9576623..d9acbf84 100644
--- a/pom.xml
+++ b/pom.xml
@@ -161,6 +161,13 @@
${evo.version}
true
+
+
+ org.projectlombok
+ lombok
+ 1.14.4
+ provided
+
org.slf4j
diff --git a/src/main/java/org/springframework/hateoas/alps/Alps.java b/src/main/java/org/springframework/hateoas/alps/Alps.java
new file mode 100644
index 00000000..44277dbe
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/alps/Alps.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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.alps;
+
+import java.util.List;
+
+import lombok.Value;
+import lombok.experimental.Builder;
+
+import org.springframework.hateoas.alps.Descriptor.DescriptorBuilder;
+import org.springframework.hateoas.alps.Doc.DocBuilder;
+import org.springframework.hateoas.alps.Ext.ExtBuilder;
+
+/**
+ * An ALPS document.
+ *
+ * @author Oliver Gierke
+ * @since 0.15
+ * @see http://alps.io
+ * @see http://alps.io/spec/#prop-alps
+ */
+@Value
+@Builder(builderMethodName = "alps")
+public class Alps {
+
+ private final String version = "1.0";
+ private final Doc doc;
+ private final List descriptors;
+
+ /**
+ * Returns a new {@link DescriptorBuilder}.
+ *
+ * @return
+ */
+ public static DescriptorBuilder descriptor() {
+ return Descriptor.builder();
+ }
+
+ /**
+ * Returns a new {@link DocBuilder}.
+ *
+ * @return
+ */
+ public static DocBuilder doc() {
+ return Doc.builder();
+ }
+
+ /**
+ * Retruns a new {@link ExtBuilder}.
+ *
+ * @return
+ */
+ public static ExtBuilder ext() {
+ return Ext.builder();
+ }
+}
diff --git a/src/main/java/org/springframework/hateoas/alps/Descriptor.java b/src/main/java/org/springframework/hateoas/alps/Descriptor.java
new file mode 100644
index 00000000..cb5c5b6f
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/alps/Descriptor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 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.alps;
+
+import java.util.List;
+
+import lombok.Value;
+import lombok.experimental.Builder;
+
+/**
+ * A value object for an ALPS descriptor.
+ *
+ * @author Oliver Gierke
+ * @since 0.15
+ * @see http://alps.io/spec/#prop-descriptor
+ */
+@Value
+@Builder
+public class Descriptor {
+
+ private final String id, href, name;
+ private final Doc doc;
+ private final Type type;
+ private final Ext ext;
+ private final String rt;
+ private final List descriptors;
+}
diff --git a/src/main/java/org/springframework/hateoas/alps/Doc.java b/src/main/java/org/springframework/hateoas/alps/Doc.java
new file mode 100644
index 00000000..bc3077d5
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/alps/Doc.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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.alps;
+
+import lombok.AllArgsConstructor;
+import lombok.Value;
+import lombok.experimental.Builder;
+
+import org.springframework.util.Assert;
+
+/**
+ * A value object for an ALPS doc element.
+ *
+ * @author Oliver Gierke
+ * @since 0.15
+ * @see http://alps.io/spec/#prop-doc
+ */
+@Value
+@Builder
+@AllArgsConstructor
+public class Doc {
+
+ private final String href, value;
+ private final Format format;
+
+ /**
+ * Creates a new {@link Doc} instance with the given value and {@link Format}.
+ *
+ * @param value must not be {@literal null} or empty.
+ * @param format must not be {@literal null}.
+ */
+ public Doc(String value, Format format) {
+
+ Assert.hasText(value, "Value must not be null or empty!");
+ Assert.notNull(format, "Format must not be null!");
+
+ this.href = null;
+ this.value = value;
+ this.format = format;
+ }
+}
diff --git a/src/main/java/org/springframework/hateoas/alps/Ext.java b/src/main/java/org/springframework/hateoas/alps/Ext.java
new file mode 100644
index 00000000..42b4f7c3
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/alps/Ext.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.alps;
+
+import lombok.Value;
+import lombok.experimental.Builder;
+
+/**
+ * A value object for an ALPS ext element.
+ *
+ * @author Oliver Gierke
+ * @since 0.15
+ * @see http://alps.io/spec/#prop-ext
+ */
+@Value
+@Builder
+public class Ext {
+
+ private final String href;
+ private final String value;
+}
diff --git a/src/main/java/org/springframework/hateoas/alps/Format.java b/src/main/java/org/springframework/hateoas/alps/Format.java
new file mode 100644
index 00000000..581941b4
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/alps/Format.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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.alps;
+
+import java.util.Locale;
+
+/**
+ * Enum for all ALPS doc formats.
+ *
+ * @author Oliver Gierke
+ * @since 0.15
+ * @see http://alps.io/spec/#prop-format
+ */
+public enum Format {
+
+ TEXT, HTML, ASCIIDOC;
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Enum#toString()
+ */
+ public String toString() {
+ return name().toLowerCase(Locale.US);
+ }
+}
diff --git a/src/main/java/org/springframework/hateoas/alps/Type.java b/src/main/java/org/springframework/hateoas/alps/Type.java
new file mode 100644
index 00000000..cee8c382
--- /dev/null
+++ b/src/main/java/org/springframework/hateoas/alps/Type.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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.alps;
+
+import java.util.Locale;
+
+/**
+ * An enum for ALPS descriptor types
+ *
+ * @author Oliver Gierke
+ * @since 0.15
+ * @see http://alps.io/spec/#prop-type
+ */
+public enum Type {
+
+ SEMANTIC, SAFE, IDEMPOTENT, UNSAFE;
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Enum#toString()
+ */
+ public String toString() {
+ return name().toLowerCase(Locale.US);
+ }
+}
diff --git a/src/test/java/org/springframework/hateoas/alps/JacksonSerializationTests.java b/src/test/java/org/springframework/hateoas/alps/JacksonSerializationTests.java
new file mode 100644
index 00000000..33c0d2a8
--- /dev/null
+++ b/src/test/java/org/springframework/hateoas/alps/JacksonSerializationTests.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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.alps;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.hateoas.alps.Alps.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Scanner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+/**
+ * Unit tests for serialization of ALPS documents.
+ *
+ * @author Oliver Gierke
+ */
+public class JacksonSerializationTests {
+
+ ObjectMapper mapper;
+
+ @Before
+ public void setUp() {
+
+ mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(Include.NON_NULL);
+ mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+ }
+
+ /**
+ * @see #141
+ */
+ @Test
+ public void writesSampleDocument() throws Exception {
+
+ Alps alps = alps().//
+ doc(doc().href("http://example.org/samples/full/doc.html").build()). //
+ descriptors(Arrays.asList(//
+ descriptor().id("search").type(Type.SAFE).//
+ doc(new Doc("A search form with two inputs.", Format.TEXT)).//
+ descriptors(Arrays.asList( //
+ descriptor().href("#resultType").build(), //
+ descriptor().id("value").name("search").type(Type.SEMANTIC).build())//
+ ).build(), //
+ descriptor().id("resultType").type(Type.SEMANTIC).//
+ doc(doc().value("results format").build()).//
+ ext(ext().href("http://alps.io/ext/range").value("summary,detail").build()//
+ ).build())//
+ ).build();
+
+ assertThat(mapper.writeValueAsString(alps), is(read(new ClassPathResource("reference.json", getClass()))));
+ }
+
+ private static String read(Resource resource) throws IOException {
+
+ Scanner scanner = null;
+
+ try {
+
+ scanner = new Scanner(resource.getInputStream());
+ StringBuilder builder = new StringBuilder();
+
+ while (scanner.hasNextLine()) {
+
+ builder.append(scanner.nextLine());
+
+ if (scanner.hasNextLine()) {
+ builder.append("\n");
+ }
+ }
+
+ return builder.toString();
+
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ }
+}
diff --git a/src/test/resources/org/springframework/hateoas/alps/reference.json b/src/test/resources/org/springframework/hateoas/alps/reference.json
new file mode 100644
index 00000000..24b2d066
--- /dev/null
+++ b/src/test/resources/org/springframework/hateoas/alps/reference.json
@@ -0,0 +1,31 @@
+{
+ "version" : "1.0",
+ "doc" : {
+ "href" : "http://example.org/samples/full/doc.html"
+ },
+ "descriptors" : [ {
+ "id" : "search",
+ "doc" : {
+ "value" : "A search form with two inputs.",
+ "format" : "TEXT"
+ },
+ "type" : "SAFE",
+ "descriptors" : [ {
+ "href" : "#resultType"
+ }, {
+ "id" : "value",
+ "name" : "search",
+ "type" : "SEMANTIC"
+ } ]
+ }, {
+ "id" : "resultType",
+ "doc" : {
+ "value" : "results format"
+ },
+ "type" : "SEMANTIC",
+ "ext" : {
+ "href" : "http://alps.io/ext/range",
+ "value" : "summary,detail"
+ }
+ } ]
+}
\ No newline at end of file