diff --git a/Helpers.jrag b/Helpers.jrag
index 8c1ff5650a1619fb604a7f96de82ca32e858ffa4..41287726d4e900a0ef096abee83f679daf74808c 100644
--- a/Helpers.jrag
+++ b/Helpers.jrag
@@ -1,20 +1,76 @@
 aspect Helpers {
+  // --- of ---
   public static ValueElement ValueElement.of(int value) {
-    return new ValueElement(String.valueOf(value));
+    return new ValueElement(false, String.valueOf(value));
   }
   public static ValueElement ValueElement.of(boolean value) {
-    return new ValueElement(String.valueOf(value));
+    return new ValueElement(false, String.valueOf(value));
   }
   public static ValueElement ValueElement.of(String value) {
-    return new ValueElement(value);
+    return new ValueElement(false, value);
   }
   public static StringElement StringElement.of(String value) {
-    return new StringElement(value);
+    return new StringElement(false, value);
   }
+
+  // --- addKeyValuePair ---
   public void MappingElement.addKeyValuePair(String key, Element value) {
     addKeyValuePair(new KeyValuePair(key, value));
   }
 
+  // --- put ---
+  public MappingElement MappingElement.put(String key, int value) {
+    addKeyValuePair(key, ValueElement.of(value));
+    return this;
+  }
+  public MappingElement MappingElement.put(String key, boolean value) {
+    addKeyValuePair(key, ValueElement.of(value));
+    return this;
+  }
+  public MappingElement MappingElement.put(String key, String value) {
+    addKeyValuePair(key, makeStringElement(value));
+    return this;
+  }
+  public MappingElement MappingElement.put(String key, Element inner) {
+    addKeyValuePair(key, inner);
+    return this;
+  }
+
+  // --- add ---
+  public ListElement ListElement.add(int value) {
+    addElement(ValueElement.of(value));
+    return this;
+  }
+  public ListElement ListElement.add(boolean value) {
+    addElement(ValueElement.of(value));
+    return this;
+  }
+  public ListElement ListElement.add(String value) {
+    addElement(makeStringElement(value));
+    return this;
+  }
+  public ListElement ListElement.add(Element inner) {
+    addElement(inner);
+    return this;
+  }
+
+  // --- helper methods for put/add ---
+  protected SimpleElement ComplexElement.makeStringElement(String value) {
+    // simple test, check for special characters
+    return containsAny(value, "[{\"\n") ?
+      StringElement.of(value.replace("\n", "\\n").replace("\"", "\\\"")) :
+      ValueElement.of(value);
+  }
+
+  protected boolean ComplexElement.containsAny(String s, String searchChars) {
+    // from https://stackoverflow.com/a/54399334/2493208
+    java.util.Set<Character> charsToTestFor = searchChars.chars()
+            .mapToObj(ch -> Character.valueOf((char) ch))
+            .collect(java.util.stream.Collectors.toSet());
+    return s.chars().anyMatch(ch -> charsToTestFor.contains(Character.valueOf((char) ch)));
+  }
+
+  // --- getValue ---
   public java.util.Optional<Element> MappingElement.getValue(String key) {
     for (KeyValuePair pair : getKeyValuePairList()) {
       if (pair.getKey().equals(key)) {
diff --git a/Mustache.relast b/Mustache.relast
index 4ffd34a97eba31b5978c93d0faaccf3327f4bde7..581dcdf509d2bb0757a8545e72a297d8a05fab69 100644
--- a/Mustache.relast
+++ b/Mustache.relast
@@ -1,5 +1,5 @@
 Document ::= <FileName> [RootElement:ComplexElement] ;
-abstract Element ;
+abstract Element ::= <Collapse:boolean> ;
 abstract ComplexElement : Element ;
 MappingElement : ComplexElement ::= KeyValuePair* ;
 KeyValuePair ::= <Key> Value:Element ;
diff --git a/Printing.jrag b/Printing.jrag
index 2b93aa2b87162bef2766b5d5e263d774a86cf06c..39deaadb67827a56e3f25af6f09017aa79cb621f 100644
--- a/Printing.jrag
+++ b/Printing.jrag
@@ -1,14 +1,25 @@
 aspect Printing {
   String ASTNode.PRINT_INDENT = "  ";
 
+  // --- isLast ---
   inh boolean KeyValuePair.isLast();
   inh boolean Element.isLast();
   eq MappingElement.getKeyValuePair(int i).isLast() = i == getNumKeyValuePair() - 1;
   eq ListElement.getElement(int i).isLast() = i == getNumElement() - 1;
   eq Document.getRootElement().isLast() = true;
+
+  // --- needTrailingNewLine ---
   syn boolean KeyValuePair.needTrailingNewLine() = !this.isLast();
   syn boolean Element.needTrailingNewLine() = !this.isLast() || containingListElement() == null;
 
+  // --- isCollapsed ---
+  inh boolean Element.isCollapsed();
+  inh boolean KeyValuePair.isCollapsed();
+  eq Document.getRootElement().isCollapsed() = getRootElement().getCollapse();
+  eq MappingElement.getKeyValuePair(int i).isCollapsed() = isCollapsed();
+  eq ListElement.getElement(int i).isCollapsed() = getElement(i).getCollapse() || isCollapsed();
+  eq KeyValuePair.getValue().isCollapsed() = getValue().getCollapse() || isCollapsed();
+
   public String Document.prettyPrint() {
     return prettyPrint(true);
   }
@@ -46,11 +57,22 @@ aspect Printing {
     if (isEmpty()) {
       sb.append("[]");
     } else {
-      for (Element element : getElementList()) {
-        sb.append(indent).append("- ");
-        element.prettyPrint(sb, false, indent + PRINT_INDENT);
-        if (element.needTrailingNewLine()) {
-          sb.append("\n");
+      if (isCollapsed()) {
+        sb.append("[");
+        for (Element element : getElementList()) {
+          element.prettyPrint(sb, false, indent);
+          if (!element.isLast()) {
+            sb.append(", ");
+          }
+        }
+        sb.append("]");
+      } else {
+        for (Element element : getElementList()) {
+          sb.append(indent).append("- ");
+          element.prettyPrint(sb, false, indent + PRINT_INDENT);
+          if (element.needTrailingNewLine()) {
+            sb.append("\n");
+          }
         }
       }
     }
@@ -59,8 +81,15 @@ aspect Printing {
 
   protected StringBuilder KeyValuePair.prettyPrint(StringBuilder sb, boolean printIndent, String indent) {
     if (printIndent) sb.append(indent);
-    sb.append(getKey()).append(":");
-    if (getValue().isComplexElement() && !getValue().isEmpty()) {
+    if (isCollapsed()) {
+      sb.append("\"");
+    }
+    sb.append(getKey());
+    if (isCollapsed()) {
+      sb.append("\"");
+    }
+    sb.append(":");
+    if (getValue().isComplexElement() && !getValue().isEmpty() && !getValue().isCollapsed()) {
       sb.append("\n");
       getValue().prettyPrint(sb, true, indent + PRINT_INDENT);
     } else {
@@ -75,13 +104,24 @@ aspect Printing {
     if (isEmpty()) {
       sb.append("{}");
     } else {
-      boolean first = true;
-      for (KeyValuePair pair : getKeyValuePairList()) {
-        if (!first || printIndent) sb.append(indent);
-        first = false;
-        pair.prettyPrint(sb, false, indent);
-        if (pair.needTrailingNewLine()) {
-          sb.append("\n");
+      if (isCollapsed()) {
+        sb.append("{");
+        for (KeyValuePair pair : getKeyValuePairList()) {
+          pair.prettyPrint(sb, false, indent);
+          if (!pair.isLast()) {
+            sb.append(", ");
+          }
+        }
+        sb.append("}");
+      } else {
+        boolean first = true;
+        for (KeyValuePair pair : getKeyValuePairList()) {
+          if (!first || printIndent) sb.append(indent);
+          first = false;
+          pair.prettyPrint(sb, false, indent);
+          if (pair.needTrailingNewLine()) {
+            sb.append("\n");
+          }
         }
       }
     }