diff --git a/dumpAst.base/src/main/jastadd/ClassAnalysis.jrag b/dumpAst.base/src/main/jastadd/ClassAnalysis.jrag
index fdd6052df4fc03bb14e50d12eb11c36ec37d5f43..6558bc1eeb13ad3f63c0413ff0e212ea0d8bd635 100644
--- a/dumpAst.base/src/main/jastadd/ClassAnalysis.jrag
+++ b/dumpAst.base/src/main/jastadd/ClassAnalysis.jrag
@@ -48,29 +48,28 @@ aspect ClassAnalysis {
               String tokenName = invokeName(annotation);
               if (tokenName.startsWith("_impl_")) {
                 String relationName = titleCase(tokenName.substring(6));
-                try {
-                  java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
+                if (present(getMethod(clazz, "get" + relationName), relationMethod -> {
                   // normal get + token-name -> singleRelation
-                  SingleRelationMethod singleRelationMethod = new SingleRelationMethod();
+                  SingleRelationMethod singleRelationMethod = getMethod(clazz, "has" + relationName).isPresent() ?
+                      new OptRelationMethod() : new SingleRelationMethod();
                   singleRelationMethod.setMethod(relationMethod);
                   singleRelationMethod.setName(relationName);
                   result.addOtherMethod(singleRelationMethod);
+                })) {
                   continue;
-                } catch (NoSuchMethodException e) {
-                  // ignore, but we know this is probably not a single relation
                 }
+                // we know here this is probably not a single or opt relation
                 // try list-relation next
-                try {
-                  java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
+                if (present(getMethod(clazz, "get" + relationName + "List"), relationMethod -> {
                   // normal get + token-name + "List" -> listRelation
                   ListRelationMethod listRelationMethod = new ListRelationMethod();
                   listRelationMethod.setMethod(relationMethod);
                   listRelationMethod.setName(relationName);
                   result.addOtherMethod(listRelationMethod);
+                })) {
                   continue;
-                } catch (NoSuchMethodException e) {
-                  // ignore, but we know this is probably not a relation at all
                 }
+                // we know here this is probably not a relation at all
               }
               IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod();
               tokenMethod.setMethod(method);
@@ -201,6 +200,20 @@ aspect ClassAnalysis {
     }
   }
 
+  private static java.util.Optional<java.lang.reflect.Method> DumpAst.getMethod(Class<?> clazz, String methodName) {
+    try {
+      java.lang.reflect.Method method = clazz.getMethod(methodName);
+      return java.util.Optional.of(method);
+    } catch (NoSuchMethodException e) {
+      return java.util.Optional.empty();
+    }
+  }
+
+  private static <T> boolean DumpAst.present(java.util.Optional<T> optional, java.util.function.Consumer<T> callback) {
+    optional.ifPresent(callback);
+    return optional.isPresent();
+  }
+
   // --- astNodeAnnotationPrefix ---
   syn String DumpAst.astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation";
   inh String DumpNode.astNodeAnnotationPrefix();
diff --git a/dumpAst.base/src/main/jastadd/DumpAst.relast b/dumpAst.base/src/main/jastadd/DumpAst.relast
index bca6fd7a7f153906a5d551f25f9b5e696b4991e1..da8cb5a2a2d943bc8cbdaa3102051a0573673749 100644
--- a/dumpAst.base/src/main/jastadd/DumpAst.relast
+++ b/dumpAst.base/src/main/jastadd/DumpAst.relast
@@ -6,13 +6,13 @@ DumpNode ::=  DumpChildNode* DumpToken* DumpRelation*
  /InvisiblePath/ ;
 
 InnerDumpNode ::= <OriginalIndex:int>;
-rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner ;
+rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner? ;
 InnerRelationDumpNode ::= <OriginalIndex:int>;
-rel InnerRelationDumpNode.DumpNode -> DumpNode ; // .ContainerOfInner*
+rel InnerRelationDumpNode.DumpNode <-> DumpNode.ContainerOfRelationInner* ;
 
 abstract DumpChildNode ::= <Name> <Computed:boolean> ;
 DumpNormalChildNode : DumpChildNode ;
-rel DumpNormalChildNode.DumpNode <-> DumpNode.ContainerOfNormalChild ;
+rel DumpNormalChildNode.DumpNode <-> DumpNode.ContainerOfNormalChild? ;
 DumpListChildNode : DumpChildNode ::= InnerDumpNode* ;
 
 abstract DumpToken ::= <Name> <Computed:boolean> ;
@@ -45,6 +45,7 @@ NormalListChildMethod : ListChildMethod ;
 NTAListChildMethod : ListChildMethod ;
 
 SingleRelationMethod : AnalysedMethod ;
+OptRelationMethod : SingleRelationMethod ;
 ListRelationMethod : AnalysedMethod ;
 
 // TODO can the refine bug also happen for refined attributes?
diff --git a/dumpAst.base/src/main/jastadd/GeneratedNavigation.jrag b/dumpAst.base/src/main/jastadd/GeneratedNavigation.jrag
index 4ef4b469c69254a667aa3449d9735b6a4a254bc6..723bfbacbb38a3682abb0b5fc6a01a20cf2bb3b2 100644
--- a/dumpAst.base/src/main/jastadd/GeneratedNavigation.jrag
+++ b/dumpAst.base/src/main/jastadd/GeneratedNavigation.jrag
@@ -36,6 +36,12 @@ aspect Navigation {
   syn boolean AnalysedMethod.isSingleRelationMethod() = false;
   eq SingleRelationMethod.isSingleRelationMethod() = true;
 
+  /** Tests if SingleRelationMethod is a OptRelationMethod.
+  *  @return 'true' if this is a OptRelationMethod, otherwise 'false'
+  */
+  syn boolean SingleRelationMethod.isOptRelationMethod() = false;
+  eq OptRelationMethod.isOptRelationMethod() = true;
+
   /** Tests if AnalysedMethod is a ListRelationMethod.
   *  @return 'true' if this is a ListRelationMethod, otherwise 'false'
   */
diff --git a/dumpAst.base/src/main/jastadd/Navigation.jrag b/dumpAst.base/src/main/jastadd/Navigation.jrag
index dc4ce9a3e857777d7305d07f48a436fb18c0bfc7..fcec323db3a60fecb4eaa93690d73cbdd4b118a0 100644
--- a/dumpAst.base/src/main/jastadd/Navigation.jrag
+++ b/dumpAst.base/src/main/jastadd/Navigation.jrag
@@ -85,10 +85,10 @@ aspect Navigation {
     return result;
   }
 
-  // --- innerNotNull ---
-  syn boolean DumpNormalRelation.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null;
-  syn boolean DumpReferenceToken.innerNotNull() = !printConfig().getRelationWithRank() && getValue() != null && getValue().getObject() != null;
-  syn boolean InnerRelationDumpNode.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null;
+  // --- innerNotNullOrEmpty ---
+  syn boolean DumpNormalRelation.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null && getDumpNode().getObject() != DumpAst.EMPTY;
+  syn boolean DumpReferenceToken.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getValue() != null && getValue().getObject() != null && getValue().getObject() != DumpAst.EMPTY;
+  syn boolean InnerRelationDumpNode.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null && getDumpNode().getObject() != DumpAst.EMPTY;
 
   // === Method naviagtion ===
   coll java.util.List<SingleChildMethod> ClassAnalysisResult.singleChildMethods() [new java.util.ArrayList<>()] root ClassAnalysisResult;
diff --git a/dumpAst.base/src/main/jastadd/Printing.jrag b/dumpAst.base/src/main/jastadd/Printing.jrag
index 2d64f5f5604f6cb16c78ab0d317aed2033349df3..74937113a800de6850174dfbf13995c38b6ea3af 100644
--- a/dumpAst.base/src/main/jastadd/Printing.jrag
+++ b/dumpAst.base/src/main/jastadd/Printing.jrag
@@ -25,10 +25,14 @@ aspect Printing {
   syn String DumpNode.label() = getLabel();
   inh String InnerDumpNode.label();
   inh String InnerRelationDumpNode.label();
-  eq DumpListChildNode.getInnerDumpNode(int index).label() = label() + "[" +
-      chooseIndex(getInnerDumpNode(index).getOriginalIndex(), index) + "]";
-  eq DumpListRelation.getInnerRelationDumpNode(int index).label() = label() + "[" +
-      chooseIndex(getInnerRelationDumpNode(index).getOriginalIndex(), index) + "]";
+  eq DumpListChildNode.getInnerDumpNode(int index).label() = label() +
+      (getInnerDumpNode(index).getDumpNode().isEmpty() ?
+          "" :
+          "[" + chooseIndex(getInnerDumpNode(index).getOriginalIndex(), index) + "]");
+  eq DumpListRelation.getInnerRelationDumpNode(int index).label() = label() +
+      (getInnerRelationDumpNode(index).getDumpNode().isEmpty() ?
+          "" :
+          "[" + chooseIndex(getInnerRelationDumpNode(index).getOriginalIndex(), index) + "]");
   eq DumpReferenceListToken.getInnerRelationDumpNode(int index).label() = label() + "[" + index + "]";
   eq InvisiblePath.getInnerRelationDumpNode(int index).label() = null;
   protected int ASTNode.chooseIndex(int originalIndex, int inheritedIndex) {
diff --git a/dumpAst.base/src/main/jastadd/TemplateContext.jrag b/dumpAst.base/src/main/jastadd/TemplateContext.jrag
index a1d09c4ecac06f97159af0265dd46ced245f4843..a559311c3c6fb8ae58a93cf19ea72a1b81ba5936 100644
--- a/dumpAst.base/src/main/jastadd/TemplateContext.jrag
+++ b/dumpAst.base/src/main/jastadd/TemplateContext.jrag
@@ -8,6 +8,11 @@ aspect TemplateContext {
     return getObject() == null;
   }
 
+  // --- isEmpty ---
+  syn boolean DumpNode.isEmpty() {
+    return getObject() == DumpAst.EMPTY;
+  }
+
   // --- isAstNode ---
   syn boolean DumpNode.isAstNode() {
     if (getObject() == null) {
diff --git a/dumpAst.base/src/main/jastadd/ToYaml.jrag b/dumpAst.base/src/main/jastadd/ToYaml.jrag
index 0df35a8701bd1a8b4d5b93ab04d47a327ce21436..ba9c7e8b72bde8a6112ee63a2c374176ef664fa6 100644
--- a/dumpAst.base/src/main/jastadd/ToYaml.jrag
+++ b/dumpAst.base/src/main/jastadd/ToYaml.jrag
@@ -71,6 +71,7 @@ aspect ToYaml {
     // attributes
     if (!fromRelation) {
       result.put("isNull", isNull());
+      result.put("isEmpty", isEmpty());
       result.put("isAstNode", isAstNode());
       result.put("labelAndTextColor", labelAndTextColor());
       result.put("stereotypeList", stereotypeList());
@@ -137,7 +138,7 @@ aspect ToYaml {
     MappingElement result = super.toYaml(fromRelation);
     // attributes
     result.put("innerNodeName", innerNodeName());
-    result.put("innerNotNull", innerNotNull());
+    result.put("innerNotNullOrEmpty", innerNotNullOrEmpty());
     result.put("outerNodeName", outerNodeName());
     return result;
   }
@@ -170,7 +171,7 @@ aspect ToYaml {
     // attributes
     result.put("bothVisible", bothVisible());
     result.put("innerNodeName", innerNodeName());
-    result.put("innerNotNull", innerNotNull());
+    result.put("innerNotNullOrEmpty", innerNotNullOrEmpty());
     return result;
   }
 
@@ -196,7 +197,7 @@ aspect ToYaml {
     // attributes
     result.put("bothVisible", bothVisible());
     result.put("innerNodeName", innerNodeName());
-    result.put("innerNotNull", innerNotNull());
+    result.put("innerNotNullOrEmpty", innerNotNullOrEmpty());
     result.put("outerNodeName", outerNodeName());
     result.put("label", label());
     return result;
diff --git a/dumpAst.base/src/main/jastadd/Transform.jadd b/dumpAst.base/src/main/jastadd/Transform.jadd
index bc8cfbaa552963df4422d901369a58438b0a8ac1..d1a71ae0cddd2b5ca54b66d2e5afc47d6e5c7023 100644
--- a/dumpAst.base/src/main/jastadd/Transform.jadd
+++ b/dumpAst.base/src/main/jastadd/Transform.jadd
@@ -53,6 +53,8 @@ aspect Transform {
     return new TransformationOptions(Source.NORMAL, false, false);
   }
 
+  public static final Object DumpAst.EMPTY = new Object();
+
   // --- transform --- (need to be a method, because it alters the AST while traversing the object structure)
   protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj)
       throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
@@ -81,7 +83,7 @@ aspect Transform {
     }
     DumpNode node;
     String objClassName;
-    if (obj == null) {
+    if (obj == null || obj == EMPTY) {
       node = null;
       objClassName = "null";
     } else {
@@ -114,8 +116,8 @@ aspect Transform {
     if (options.invisible || !isTypeEnabled(objClassName)) {
       node.setInvisible(true);
     }
-    if (obj == null) {
-      // for a null object, we do not need any further analysis
+    if (obj == null || obj == EMPTY) {
+      // for a null or empty object, we do not need any further analysis
       return node;
     }
     final ClassAnalysisResult car = analyzeClass(obj.getClass());
@@ -143,7 +145,7 @@ aspect Transform {
         DumpNode targetNode = transform(tti, target, options.asNormal(false).allowNullObjectsOnce());
         if (targetNode != null) {
           DumpNormalChildNode normalChild = new DumpNormalChildNode();
-          normalChild.setName(childName);
+          normalChild.setName(childName + (containmentMethod.isOptChildMethod() ? "?" : ""));
           normalChild.setDumpNode(targetNode);
           normalChild.setComputed(false);
           node.addDumpChildNode(normalChild);
@@ -166,9 +168,11 @@ aspect Transform {
             listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode).setOriginalIndex(index));
           }
         }
-        if (listChild.getNumInnerDumpNode() > 0) {
-          node.addDumpChildNode(listChild);
+        if (listChild.getNumInnerDumpNode() == 0) {
+          listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(transform(tti, EMPTY,
+              options.asNormal(false).allowNullObjectsOnce())));
         }
+        node.addDumpChildNode(listChild);
       } else {
         throw new RuntimeException("Unknown containment method type " + containmentMethod);
       }
@@ -222,7 +226,8 @@ aspect Transform {
         DumpNode targetNode = transform(tti, target, options.asRelation());
         if (targetNode != null) {
           DumpNormalRelation normalRelation = new DumpNormalRelation();
-          normalRelation.setName(otherMethod.getName());
+          normalRelation.setName(otherMethod.getName() +
+              (otherMethod.asSingleRelationMethod().isOptRelationMethod() ? "?" : ""));
           normalRelation.setDumpNode(targetNode);
           node.addDumpRelation(normalRelation);
         }
@@ -243,9 +248,11 @@ aspect Transform {
                 .setOriginalIndex(index));
           }
         }
-        if (listRelation.getNumInnerRelationDumpNode() > 0) {
-          node.addDumpRelation(listRelation);
+        if (listRelation.getNumInnerRelationDumpNode() == 0) {
+          listRelation.addInnerRelationDumpNode(new InnerRelationDumpNode().setDumpNode(transform(tti, EMPTY,
+              options.asNormal(false).allowNullObjectsOnce())));
         }
+        node.addDumpRelation(listRelation);
       } else if (otherMethod.isTokenMethod()) {
         // -- token --
         TokenMethod tokenMethod = otherMethod.asTokenMethod();
diff --git a/dumpAst.base/src/main/resources/dumpAst.mustache b/dumpAst.base/src/main/resources/dumpAst.mustache
index da993c1d4d995ea2f8b15f4129207be3bc14d08a..80010c0882ada38aafc77e43b4f2a2f79dd091ce 100644
--- a/dumpAst.base/src/main/resources/dumpAst.mustache
+++ b/dumpAst.base/src/main/resources/dumpAst.mustache
@@ -22,6 +22,9 @@ scale {{{scale}}}
     {{#isNull}}
 object "null" as {{{name}}}<<null>>
     {{/isNull}}
+    {{#isEmpty}}
+object "[]" as {{{name}}}<<null>>
+    {{/isEmpty}}
     {{#isAstNode}}
 object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroundColor}}#{{{backgroundColor}}}{{/backgroundColor}} {
       {{#DumpTokens}}
@@ -40,13 +43,13 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun
       {{#isList}}
         {{#InnerRelationDumpNode}}
           {{#bothVisible}}
-{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}}
+{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNullOrEmpty}},norank{{/innerNotNullOrEmpty}}].> {{{innerNodeName}}} : {{{label}}}
           {{/bothVisible}}
         {{/InnerRelationDumpNode}}
       {{/isList}}
       {{^isList}}
         {{^isDumpValueToken}}
-{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}}
+{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNullOrEmpty}},norank{{/innerNotNullOrEmpty}}].> {{{innerNodeName}}} : {{{label}}}
         {{/isDumpValueToken}}
       {{/isList}}
     {{/invisible}}
@@ -69,13 +72,13 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun
     {{#isList}}
       {{#InnerRelationDumpNode}}
         {{#bothVisible}}
-{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}}
+{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNullOrEmpty}}[norank]{{/innerNotNullOrEmpty}}-> {{{innerNodeName}}} : {{{label}}}
         {{/bothVisible}}
       {{/InnerRelationDumpNode}}
     {{/isList}}
     {{^isList}}
       {{#bothVisible}}
-{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}}
+{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNullOrEmpty}}[norank]{{/innerNotNullOrEmpty}}-> {{{innerNodeName}}} : {{{label}}}
       {{/bothVisible}}
     {{/isList}}
   {{/DumpRelations}}
diff --git a/dumpAst.base/src/main/resources/dumpAstVersion.properties b/dumpAst.base/src/main/resources/dumpAstVersion.properties
index a399bec009f8841666c286fea1666bc282fe6fa5..9cbc2fe8b6856a6b73f2780b3e8765a417f2394e 100644
--- a/dumpAst.base/src/main/resources/dumpAstVersion.properties
+++ b/dumpAst.base/src/main/resources/dumpAstVersion.properties
@@ -1,2 +1,2 @@
-#Thu Oct 06 10:00:14 CEST 2022
-version=2.0.2
+#Fri Oct 07 16:26:50 CEST 2022
+version=2.0.3