Repository: marklogic/java-client-api
Issue: DocumentPatchBuilder.replaceValue cannot replace boolean values

This is probably actually a bug in the REST API but I found it in Java.

```
patchBuilder.replaceValue("/accepted", true);
patchBuilder.replaceValue("/accepted", new Boolean(true));
```

Both of these insert a string value into the JSON property rather than a boolean.

A workaround is to use replaceFragment
patchBuilder.replaceFragment("/accepted",  true)   

but this is a hackish workaround -- it works because the string "true", unquoted, evaluates to a boolean in constructing the patch.


Changed files:
src/main/java/com/marklogic/client/impl/DocumentPatchBuilderImpl.java, src/main/java/com/marklogic/client/impl/JSONStringWriter.java, src/test/java/com/marklogic/client/test/JSONDocumentTest.java

diff --git a/src/main/java/com/marklogic/client/impl/DocumentPatchBuilderImpl.java b/src/main/java/com/marklogic/client/impl/DocumentPatchBuilderImpl.java
index 37748044c..3c25a176a 100644
--- a/src/main/java/com/marklogic/client/impl/DocumentPatchBuilderImpl.java
+++ b/src/main/java/com/marklogic/client/impl/DocumentPatchBuilderImpl.java
@@ -86,7 +86,8 @@ implements DocumentPatchBuilder
 		String      selectPath;
 		Cardinality cardinality;
 		boolean     isFragment = true;
-		String      input;
+		Object      input;
+		String      inputAsString;
 		ContentReplaceOperation(String selectPath, Cardinality cardinality, boolean isFragment,
 				Object input
 				) {
@@ -94,15 +95,23 @@ implements DocumentPatchBuilder
 			this.selectPath  = selectPath;
 			this.cardinality = cardinality;
 			this.isFragment  = isFragment;
-			this.input       = (input instanceof String) ?
-					(String) input : input.toString();
+			this.input       = input;
+			this.inputAsString = null;
+			if (input != null) {
+				inputAsString = (input instanceof String)  ? (String) input : input.toString();
+			}
 		}
 		@Override
 		public void write(JSONStringWriter serializer) {
 			writeStartReplace(serializer, selectPath, cardinality);
 			serializer.writeStartEntry("content");
 			if (isFragment) {
-				serializer.writeFragment(input);
+				serializer.writeFragment(inputAsString);
+			} else if (input instanceof Boolean) {
+				serializer.writeBooleanValue(input);
+			}
+			else if (input instanceof Number) {
+				serializer.writeNumberValue(input);
 			} else {
 				serializer.writeStringValue(input);
 			}
@@ -115,9 +124,9 @@ implements DocumentPatchBuilder
 			writeStartReplace(out, selectPath, cardinality);
 			if (isFragment) {
 				serializer.writeCharacters(""); // force the tag close
-				out.getWriter().write(input);
+				out.getWriter().write(inputAsString);
 			} else {
-				serializer.writeCharacters(input);
+				serializer.writeCharacters(inputAsString);
 			}
 			serializer.writeEndElement();
 		}
diff --git a/src/main/java/com/marklogic/client/impl/JSONStringWriter.java b/src/main/java/com/marklogic/client/impl/JSONStringWriter.java
index 49913a66b..4af11a02a 100644
--- a/src/main/java/com/marklogic/client/impl/JSONStringWriter.java
+++ b/src/main/java/com/marklogic/client/impl/JSONStringWriter.java
@@ -153,4 +153,5 @@ public class JSONStringWriter {
 
 		return out.toString();
 	}
+
 }
diff --git a/src/test/java/com/marklogic/client/test/JSONDocumentTest.java b/src/test/java/com/marklogic/client/test/JSONDocumentTest.java
index 80967104e..fe43c5e7e 100644
--- a/src/test/java/com/marklogic/client/test/JSONDocumentTest.java
+++ b/src/test/java/com/marklogic/client/test/JSONDocumentTest.java
@@ -35,6 +35,7 @@ import org.xml.sax.SAXException;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.BooleanNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.marklogic.client.document.DocumentManager.Metadata;
 import com.marklogic.client.document.DocumentMetadataPatchBuilder.Cardinality;
@@ -153,6 +154,9 @@ public class JSONDocumentTest {
 		fragment = mapper.writeValueAsString(fragmentNode);
 		patchBldr.insertFragment("$.[\\"arrayKey\\"]", Position.LAST_CHILD,
 				Cardinality.ZERO_OR_ONE, fragment);
+		patchBldr.replaceValue("$.booleanKey", true);
+		patchBldr.replaceValue("$.numberKey2", 2);
+		patchBldr.replaceValue("$.nullKey", null);
 
 		DocumentPatchHandle patchHandle = patchBldr.pathLanguage(
 				PathLanguage.JSONPATH).build();
@@ -179,6 +183,10 @@ public class JSONDocumentTest {
 		childNode.put("appendedKey", "appended item");
 		childArray.add(childNode);
 		expectedNode.set("arrayKey", childArray);
+		expectedNode.put("booleanKey", true);
+		expectedNode.put("numberKey2", 2);
+		expectedNode.putNull("nullKey");
+
 
 		String docText = docMgr.read(docId, new StringHandle()).get();
 		assertNotNull("Read null string for patched JSON content", docText);
@@ -272,6 +280,9 @@ public class JSONDocumentTest {
 		fragment = mapper.writeValueAsString(fragmentNode);
 		patchBldr.insertFragment("/array-node('arrayKey')", Position.BEFORE,
 				fragment);
+		patchBldr.replaceValue("/booleanKey", true);
+		patchBldr.replaceValue("/numberKey2", 2);
+		patchBldr.replaceValue("/nullKey", null);
 
 		//patchBldr.replaceApply("/node()/arrayKey/node()[string(.) eq '3']",
 		//		patchBldr.call().add(2));
@@ -306,18 +317,19 @@ public class JSONDocumentTest {
 		childNode.put("appendedKey", "appended item");
 		childArray.add(childNode);
 		expectedNode.set("arrayKey", childArray);
+		expectedNode.put("booleanKey", true);
+		expectedNode.put("numberKey2", 2);
+		expectedNode.putNull("nullKey");
 
 		String docText = docMgr.read(docId, new StringHandle()).get();
-		
+
 		assertNotNull("Read null string for patched JSON content", docText);
 		JsonNode readNode = mapper.readTree(docText);
-		
 
 		logger.debug("Before3:" + content);
 		logger.debug("After3:"+docText);
 		logger.debug("Expected3:" + mapper.writeValueAsString(expectedNode));
-		
-		
+
 		assertTrue("Patched JSON document without expected result",
 				expectedNode.equals(readNode));
 
@@ -400,6 +412,9 @@ public class JSONDocumentTest {
 		childNode.put("itemObjectKey", "item object value");
 		childArray.add(childNode);
 		sourceNode.set("arrayKey", childArray);
+		sourceNode.put("booleanKey", false);
+		sourceNode.put("numberKey2", 1);
+		sourceNode.put("nullKey", 0);
 
 		return sourceNode;
 	}