Added arbitrary element with attribute(s) deserialization and test examples.
diff --git a/src/main/java/de/ids_mannheim/korap/KorapQuery.java b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
index d18d9cb..d2a1660 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
@@ -12,7 +12,21 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 
 import de.ids_mannheim.korap.query.SpanWithinQuery;
-import de.ids_mannheim.korap.query.wrap.*;
+import de.ids_mannheim.korap.query.wrap.SpanAlterQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanAttributeQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanClassQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanElementQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanFocusQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanRegexQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanRepetitionQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanSegmentQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanSequenceQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanSimpleQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanSubspanQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanWildcardQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanWithAttributeQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanWithinQueryWrapper;
 import de.ids_mannheim.korap.response.Notifications;
 import de.ids_mannheim.korap.util.QueryException;
 
@@ -944,78 +958,103 @@
 						"JSON-LD group has no @type attribute");
 			}
 
-			SpanQueryWrapper elementWithIdWrapper = null;
 			if (value.toString().isEmpty()) {
-				// attribute with arbitraty elements
-
-				this.addWarning(771,
-						"Arbitraty elements with attributes are currently not supported.");
-				return null;
+				return createElementAttrFromJson(null, json, attrNode);
+				// this.addWarning(771,
+				// "Arbitraty elements with attributes are currently not supported.");
 			}
 			else{
-				elementWithIdWrapper = tag(value.toString());
-				if (elementWithIdWrapper == null) return null;
+				SpanQueryWrapper elementWithIdWrapper = tag(value.toString());
+				if (elementWithIdWrapper == null){ return null; }
+				return createElementAttrFromJson(elementWithIdWrapper, json,
+						attrNode);
 			}
-
-			if (attrNode.get("@type").asText().equals("koral:term")) {
-				SpanQueryWrapper attrWrapper = _attrFromJson(json.get("attr"));
-				if (attrWrapper != null) {
-					return new SpanWithAttributeQueryWrapper(
-							elementWithIdWrapper, attrWrapper);
-				}
-				else {
-					throw new QueryException(747, "Attribute is null");
-				}
-			} 
-			else if (attrNode.get("@type").asText().equals("koral:termGroup")) {
-				if (!attrNode.has("relation")) {
-					throw new QueryException(743, "Term group expects a relation");
-				}
-
-				if (!attrNode.has("operands")) {
-					throw new QueryException(742, "Term group needs operand list");
-				}
-
-				String relation = attrNode.get("relation").asText();
-				JsonNode operands = attrNode.get("operands");
-
-				SpanQueryWrapper attrWrapper;
-				if ("relation:and".equals(relation)) {
-					List<SpanQueryWrapper> wrapperList = new ArrayList<SpanQueryWrapper>();
-					for (JsonNode operand : operands) {
-						attrWrapper = _termFromJson(operand);
-						if (attrWrapper == null) {
-							throw new QueryException(747, "Attribute is null");
-						}
-						wrapperList.add(attrWrapper);
-					}
-
-					return new SpanWithAttributeQueryWrapper(
-							elementWithIdWrapper, wrapperList);
-				}
-				else if ("relation:or".equals(relation)){
-					SpanAlterQueryWrapper saq = new SpanAlterQueryWrapper(field);
-					for (JsonNode operand : operands) {
-						attrWrapper = _termFromJson(operand);
-						if (attrWrapper == null) {
-							throw new QueryException(747, "Attribute is null");
-						}
-						saq.or(new SpanWithAttributeQueryWrapper(
-								elementWithIdWrapper, attrWrapper));
-					}
-					return saq;
-				}
-				else {
-					throw new QueryException(716, "Unknown relation");
-				}
-			}
-			else {
-	            this.addWarning(715, "Attribute type is not supported");
-	        }
         };
         return this.tag(value.toString());
     };
 
+	private SpanQueryWrapper createElementAttrFromJson(
+			SpanQueryWrapper elementWithIdWrapper, JsonNode json,
+			JsonNode attrNode) throws QueryException {
+
+		if (attrNode.get("@type").asText().equals("koral:term")) {
+			SpanQueryWrapper attrWrapper = _attrFromJson(json.get("attr"));
+			if (attrWrapper != null) {
+				if (elementWithIdWrapper != null){
+					return new SpanWithAttributeQueryWrapper(elementWithIdWrapper,
+						attrWrapper);
+				}
+				else {
+					return new SpanWithAttributeQueryWrapper(attrWrapper);
+				}
+			} 
+			else {
+				throw new QueryException(747, "Attribute is null");
+			}
+		} 
+ else if (attrNode.get("@type").asText().equals("koral:termGroup")) {
+			return handleAttrGroup(elementWithIdWrapper, attrNode);
+		}
+		else {
+			this.addWarning(715, "Attribute type is not supported");
+		}
+		return elementWithIdWrapper;
+	}
+	
+	private SpanQueryWrapper handleAttrGroup(
+			SpanQueryWrapper elementWithIdWrapper, JsonNode attrNode)
+			throws QueryException {
+		if (!attrNode.has("relation")) {
+			throw new QueryException(743, "Term group expects a relation");
+		}
+		if (!attrNode.has("operands")) {
+			throw new QueryException(742, "Term group needs operand list");
+		}
+
+		String relation = attrNode.get("relation").asText();
+		JsonNode operands = attrNode.get("operands");
+
+		SpanQueryWrapper attrWrapper;
+		if ("relation:and".equals(relation)) {
+			List<SpanQueryWrapper> wrapperList = new ArrayList<SpanQueryWrapper>();
+			for (JsonNode operand : operands) {
+				attrWrapper = _termFromJson(operand);
+				if (attrWrapper == null) {
+					throw new QueryException(747, "Attribute is null");
+				}
+				wrapperList.add(attrWrapper);
+			}
+
+			if (elementWithIdWrapper != null){
+				return new SpanWithAttributeQueryWrapper(elementWithIdWrapper,
+					wrapperList);
+			}
+			else {
+				return new SpanWithAttributeQueryWrapper(wrapperList);
+			}
+		} 
+		else if ("relation:or".equals(relation)) {
+			SpanAlterQueryWrapper saq = new SpanAlterQueryWrapper(field);
+			SpanWithAttributeQueryWrapper saqw;
+			for (JsonNode operand : operands) {
+				attrWrapper = _termFromJson(operand);
+				if (attrWrapper == null) {
+					throw new QueryException(747, "Attribute is null");
+				}
+				if (elementWithIdWrapper != null) {
+					saqw = new SpanWithAttributeQueryWrapper(
+							elementWithIdWrapper, attrWrapper);
+				} else {
+					saqw = new SpanWithAttributeQueryWrapper(attrWrapper);
+				}
+				saq.or(saqw);
+			}
+			return saq;
+		}
+		else {
+			throw new QueryException(716, "Unknown relation");
+		}
+	}
 
     // Get attributes from a json termgroup
     private SpanQueryWrapper _attrFromJson (JsonNode attrNode)
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java
index 7b7418f..7b633c0 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java
@@ -23,12 +23,16 @@
 	private List<SpanQueryWrapper> queryWrapperList = null;
 	private boolean isSingleAttribute = false;
 
-	public SpanWithAttributeQueryWrapper(SpanQueryWrapper attrQuery) {
+	public SpanWithAttributeQueryWrapper(SpanQueryWrapper attrQuery)
+			throws QueryException {
 		if (attrQuery != null) isNull = false;
 		if (attrQuery.isEmpty()) {
 			isEmpty = true;
 			return;
 		}
+		if (attrQuery.isNegative) {
+			throw new QueryException("The query requires a positive attribute.");
+		}
 		this.attrQueryWrapper = attrQuery;
 		this.isSingleAttribute = true;
 	}
@@ -41,6 +45,7 @@
 			throw new QueryException("No attribute is defined.");
 		}
 
+		boolean isAllNegative = true;
 		for (SpanQueryWrapper sqw : attrList) {
 			if (sqw == null) {
 				isNull = true;
@@ -50,6 +55,12 @@
 				isEmpty = true;
 				return;
 			}
+			if (!sqw.isNegative) {
+				isAllNegative = false;
+			}
+		}
+		if (isAllNegative) {
+			throw new QueryException("No positive attribute is defined.");
 		}
 		this.queryWrapperList = attrList;
 	}
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanWithAttributeJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanWithAttributeJSON.java
index a04e6f7..976958a 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestSpanWithAttributeJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanWithAttributeJSON.java
@@ -67,8 +67,46 @@
 				"/queries/attribute/any-element-with-attribute.jsonld")
 				.getFile();
 		SpanQueryWrapper sqwi = getJSONQuery(filepath);
-		// SpanQuery sq = sqwi.toQuery();
-		assertEquals(null, sqwi);
+		SpanQuery sq = sqwi.toQuery();
+		assertEquals(
+				"spanWithAttribute(spanAttribute(tokens:type:top))",
+				sq.toString());
+	}
+
+	@Test
+	public void testAnyElementWithMultipleOrAttributes() throws QueryException {
+		String filepath = getClass().getResource(
+						"/queries/attribute/any-element-with-multiple-or-attributes.jsonld")
+				.getFile();
+		SpanQueryWrapper sqwi = getJSONQuery(filepath);
+		SpanQuery sq = sqwi.toQuery();
+		assertEquals(
+				"spanOr([spanWithAttribute(spanAttribute(tokens:type:Zeitschrift)), "
+						+ "spanWithAttribute(spanAttribute(tokens:complete:Y)), "
+						+ "spanWithAttribute(spanAttribute(tokens:n:0))])",
+				sq.toString());
+	}
+
+	@Test
+	public void testAnyElementMultipleAndNotAttributes() throws QueryException {
+		String filepath = getClass()
+				.getResource(
+						"/queries/attribute/any-element-with-multiple-and-not-attributes.jsonld")
+				.getFile();
+		SpanQueryWrapper sqwi = getJSONQuery(filepath);
+		SpanQuery sq = sqwi.toQuery();
+		assertEquals(
+				"spanWithAttribute([spanAttribute(tokens:type:Zeitschrift), "
+						+ "spanAttribute(!tokens:complete:Y), spanAttribute(tokens:n:0)])",
+				sq.toString());
+	}
+
+	@Test(expected = QueryException.class)
+	public void testAnyElementSingleNotAttribute() throws QueryException {
+		String filepath = getClass().getResource(
+						"/queries/attribute/any-element-with-single-not-attribute.jsonld")
+				.getFile();
+		SpanQueryWrapper sqwi = getJSONQuery(filepath);
 	}
 
 }
diff --git a/src/test/resources/queries/attribute/any-element-with-attribute.jsonld b/src/test/resources/queries/attribute/any-element-with-attribute.jsonld
index ead458e..f53906d 100644
--- a/src/test/resources/queries/attribute/any-element-with-attribute.jsonld
+++ b/src/test/resources/queries/attribute/any-element-with-attribute.jsonld
@@ -8,11 +8,9 @@
         "@type": "koral:span",
         "attr": {
             "@type": "koral:term",
-            "arity": {
-                "@type": "koral:boundary",
-                "min": 2,
-                "max": 2
-            }
+            "layer": "type",
+            "key": "top",
+            "match": "match:eq"
         }
     },
     "meta": {}
diff --git a/src/test/resources/queries/attribute/any-element-with-multiple-and-not-attributes.jsonld b/src/test/resources/queries/attribute/any-element-with-multiple-and-not-attributes.jsonld
new file mode 100644
index 0000000..db21c70
--- /dev/null
+++ b/src/test/resources/queries/attribute/any-element-with-multiple-and-not-attributes.jsonld
@@ -0,0 +1,35 @@
+{
+    "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+    "errors": [],
+    "warnings": [],
+    "messages": [],
+    "collection": {},
+    "query": {
+        "@type": "korap:span",
+       	"attr": {
+            "@type": "korap:termGroup",
+            "relation": "relation:and",
+            "operands": [
+                {
+                    "@type": "korap:term",
+                    "layer": "type",
+                    "key": "Zeitschrift",
+                    "match": "match:eq"
+                },
+                {
+                    "@type": "korap:term",
+                    "layer": "complete",
+                    "key": "Y",
+                    "match": "match:ne"
+                },
+                {
+                    "@type": "korap:term",
+                    "layer": "n",
+                    "key": "0",
+                    "match": "match:eq"
+                }
+            ]
+        }
+    },
+    "meta": {}
+}
\ No newline at end of file
diff --git a/src/test/resources/queries/attribute/any-element-with-multiple-or-attributes.jsonld b/src/test/resources/queries/attribute/any-element-with-multiple-or-attributes.jsonld
new file mode 100644
index 0000000..765531c
--- /dev/null
+++ b/src/test/resources/queries/attribute/any-element-with-multiple-or-attributes.jsonld
@@ -0,0 +1,35 @@
+{
+    "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+    "errors": [],
+    "warnings": [],
+    "messages": [],
+    "collection": {},
+    "query": {
+        "@type": "korap:span",
+        "attr": {
+            "@type": "korap:termGroup",
+            "relation": "relation:or",
+            "operands": [
+                {
+                    "@type": "korap:term",
+                    "layer": "type",
+                    "key": "Zeitschrift",
+                    "match": "match:eq"
+                },
+                {
+                    "@type": "korap:term",
+                    "layer": "complete",
+                    "key": "Y",
+                    "match": "match:eq"
+                },
+                {
+                    "@type": "korap:term",
+                    "layer": "n",
+                    "key": "0",
+                    "match": "match:eq"
+                }
+            ]
+        }
+    },
+    "meta": {}
+}
\ No newline at end of file
diff --git a/src/test/resources/queries/attribute/any-element-with-single-not-attribute.jsonld b/src/test/resources/queries/attribute/any-element-with-single-not-attribute.jsonld
new file mode 100644
index 0000000..3c1ed4f
--- /dev/null
+++ b/src/test/resources/queries/attribute/any-element-with-single-not-attribute.jsonld
@@ -0,0 +1,17 @@
+{
+    "@context": "http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+    "errors": [],
+    "warnings": [],
+    "messages": [],
+    "collection": {},
+    "query": {
+        "@type": "korap:span",        
+        "attr": {
+            "@type": "korap:term",
+            "layer": "type",
+            "key": "top",
+            "match": "match:ne"
+        }
+    },
+    "meta": {}
+}
\ No newline at end of file