diff --git a/src/main/java/org/extendj/ragdoc/JsonBuilder.java b/src/main/java/org/extendj/ragdoc/JsonBuilder.java
index 78ff6ec57c92136183aa04ae46dc47e8f47f14b8..0b0ac40f29914cf71597d25caa819789a3e0c63f 100644
--- a/src/main/java/org/extendj/ragdoc/JsonBuilder.java
+++ b/src/main/java/org/extendj/ragdoc/JsonBuilder.java
@@ -57,6 +57,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 
 public class JsonBuilder {
   private Map<String, String> typeIndex = new HashMap<>();
@@ -65,6 +66,7 @@ public class JsonBuilder {
   private Set<String> aspects = new HashSet<>();
   final Map<String, JsonArray> packages = new HashMap<>();
   final Map<String, Collection<TypeDecl>> packageTypeMap = new HashMap<>();
+  final Map<TypeDecl, Collection<TypeDecl>> subtypeMap = new HashMap<>();
 
   /** Ordering of the package object kinds. */
   private static final String[] TYPE_KINDS = { "ast-class", "interface", "class" };
@@ -144,7 +146,7 @@ public class JsonBuilder {
     }
   }
 
-  private JsonValue typeRef(TypeDecl type) {
+  private JsonObject typeRef(TypeDecl type) {
     // TODO: handle wildcard types.
     JsonObject obj = new JsonObject();
     if (shouldDocument(type)) {
@@ -201,6 +203,14 @@ public class JsonBuilder {
   }
 
   // TODO: inner class names.
+
+  /**
+   * Generates a unique suffix that is used to distinguish duplicate typenames.
+   *
+   * @param type the type to generate a unique name suffix for.
+   * @return {@code "%"} if the given type has a unique typename, otherwise
+   * {@code "%ID"} where the ID part is a unique integer.
+   */
   String typeId(TypeDecl type) {
     String typename = type.baseTypeName();
     if (typeIndex.containsKey(typename)) {
@@ -336,10 +346,12 @@ public class JsonBuilder {
       ClassDecl klass = (ClassDecl) type;
       if (klass.hasSuperclass()) {
         obj.add("superclass", typeRef(klass.superclass()));
+        addSubtype(klass.superclass(), type);
       }
       JsonArray ifaces = new JsonArray();
       for (Access access: klass.getImplementsList()) {
         ifaces.add(typeRef(access.type()));
+        addSubtype(access.type(), type);
       }
       if (!ifaces.isEmpty()) {
         obj.add("superinterfaces", ifaces);
@@ -383,7 +395,20 @@ public class JsonBuilder {
   }
 
   /**
-   * This adds inherited members from superclasses.
+   * Registers subtype as being a subtype of type.
+   */
+  private void addSubtype(TypeDecl type, TypeDecl subtype) {
+    type = type.original();
+    Collection<TypeDecl> subtypes = subtypeMap.get(type);
+    if (subtypes == null) {
+      subtypes = new HashSet<>();
+      subtypeMap.put(type, subtypes);
+    }
+    subtypes.add(subtype);
+  }
+
+  /**
+   * Adds inherited members from superclasses.
    */
   private void addInheritedMembers(TypeDecl type, JsonObject obj) {
     if (type instanceof ClassDecl) {
@@ -478,9 +503,16 @@ public class JsonBuilder {
       if (groupMap.containsKey(kind)) {
         JsonObject group = new JsonObject();
         group.add("kind", Json.of(kind));
-        JsonArray members = groupMap.get(kind);
-        sortArrayBy(members, "name");
-        group.add("members", members);
+        JsonArray sortedMembers = new JsonArray();
+        KeyFun<JsonValue, String> keyFun = new KeyFun<JsonValue, String>() {
+          @Override public String apply(JsonValue value) {
+            return value.object().get("name").asString("");
+          }
+        };
+        for (JsonValue v : sortBy(groupMap.get(kind), keyFun)) {
+          sortedMembers.add(v);
+        }
+        group.add("members", sortedMembers);
         groups.add(group);
       }
     }
@@ -489,7 +521,7 @@ public class JsonBuilder {
 
   public JsonArray packageIndex() {
     // TODO: split into separate arrays based on object kind.
-    JsonArray packageIndex = new JsonArray();
+    List<JsonObject> packageIndex = new ArrayList<>();
     for (String packageName : packages.keySet()) {
       // Group members by kind.
       Map<String, JsonArray> groupMap = new HashMap<>();
@@ -512,27 +544,62 @@ public class JsonBuilder {
       entry.add("groups", groups);
       packageIndex.add(entry);
     }
-    sortArrayBy(packageIndex, "name");
-    return packageIndex;
+    JsonArray sorted = new JsonArray();
+    KeyFun<JsonObject, String> keyFun = new KeyFun<JsonObject, String>() {
+      @Override public String apply(JsonObject value) {
+        return value.object().get("name").asString("");
+      }
+    };
+    for (JsonValue v : sortBy(packageIndex, keyFun)) {
+      sorted.add(v);
+    }
+    return sorted;
+  }
+
+  public JsonArray subtypesJson(TypeDecl type) {
+    JsonArray subtypes = new JsonArray();
+    if (subtypeMap.containsKey(type)) {
+      KeyFun<TypeDecl, String> keyFun = new KeyFun<TypeDecl, String>() {
+        @Override public String apply(TypeDecl type) {
+          return type.name();
+        }
+      };
+      for (TypeDecl subtype : sortBy(subtypeMap.get(type), keyFun)) {
+        subtypes.add(typeRef(subtype));
+      }
+    }
+    return subtypes;
+  }
+
+  public interface KeyFun<T, R> {
+    R apply(T t);
   }
 
   /**
-   * Sort object elements of JSON array by a particular key.
+   * Returns a sorted JSON array based on the argument elements and a key function.
+   *
+   * <p>The result preserves duplicate elements and elements with identical keys.</p>
    */
-  static void sortArrayBy(JsonArray array, String key) {
-    Map<String, JsonObject> members = new HashMap<>();
-    for (JsonValue value : array) {
-      JsonObject member = value.object();
-      members.put(member.get(key).stringValue(""), member);
+  static <T, K extends Comparable<? super K>> List<T> sortBy(
+      Iterable<T> iterable,
+      KeyFun<T, K> keyFun) {
+    Map<K, Collection<T>> members = new TreeMap<>();
+    for (T value : iterable) {
+      K sortKey = keyFun.apply(value);
+      Collection<T> items = members.get(sortKey);
+      if (items == null) {
+        items = new ArrayList<>();
+        members.put(sortKey, items);
+      }
+      items.add(value);
     }
-    List<String> names = new ArrayList<>(members.keySet());
-    Collections.sort(names);
-    int index = 0;
-    for (String name : names) {
-      JsonObject member = members.get(name);
-      array.set(index, member);
-      index += 1;
+    List<K> keys = new ArrayList<>(members.keySet());
+    Collections.sort(keys);
+    ArrayList<T> result = new ArrayList<>(members.size());
+    for (Collection<T> values : members.values()) {
+      result.addAll(values);
     }
+    return result;
   }
 
 }
diff --git a/src/main/java/org/extendj/ragdoc/RagDocBuilder.java b/src/main/java/org/extendj/ragdoc/RagDocBuilder.java
index bc48412975c0bf456f96f9d29d2fa1d45ae836eb..d2be8b2585678178bb43e392b70d11fa0a32a754 100644
--- a/src/main/java/org/extendj/ragdoc/RagDocBuilder.java
+++ b/src/main/java/org/extendj/ragdoc/RagDocBuilder.java
@@ -37,6 +37,7 @@ import org.extendj.ast.JavaParser;
 import org.extendj.ast.Problem;
 import org.extendj.ast.Program;
 import org.extendj.ast.TypeDecl;
+import se.llbit.json.JsonArray;
 import se.llbit.json.JsonObject;
 import se.llbit.json.JsonValue;
 import se.llbit.json.PrettyPrinter;
@@ -110,7 +111,10 @@ public class RagDocBuilder extends Frontend {
       for (Map.Entry<TypeDecl, JsonObject> entry : jsonBuilder.typemap.entrySet()) {
         TypeDecl type = entry.getKey();
         JsonObject typeJson = entry.getValue();
-        JsonBuilder.sortArrayBy(typeJson.get("members").array(), "name");
+        JsonArray subtypes = jsonBuilder.subtypesJson(type);
+        if (!subtypes.isEmpty()) {
+          typeJson.add("subtypes", jsonBuilder.subtypesJson(type));
+        }
         String fileName = type.name() + jsonBuilder.typeId(type).substring(1) + ".json";
         outputJson(outputDir, fileName, typeJson);
       }