Skip to content

Commit a6126d5

Browse files
author
Charles Greer
committed
DocumentPatchBuilder JSONPath and XPath for JSON
1 parent 2db6d8e commit a6126d5

File tree

6 files changed

+354
-44
lines changed

6 files changed

+354
-44
lines changed

src/main/java/com/marklogic/client/document/DocumentPatchBuilder.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,36 @@ public String toString() {
5757
}
5858
}
5959

60+
61+
/**
62+
* MarkLogic's REST API patch operations support two path languages for JSON,
63+
* XPATH and JSONPATH. Default for MarkLogic 8 is XPATH,
64+
* but you can use the backwards-compatible JSONPATH too.
65+
*/
66+
public enum PathLanguage {
67+
68+
/**
69+
* Indicates that the given patch uses the XPATH language.
70+
*/
71+
XPATH,
72+
73+
/**
74+
* Indicates that the given patch uses the JSONPATH language.
75+
*/
76+
JSONPATH;
77+
78+
@Override
79+
public String toString() {
80+
return super.toString().toLowerCase();
81+
};
82+
83+
}
84+
85+
/**
86+
* Specifies the language for this patch to use
87+
*/
88+
public DocumentPatchBuilder pathLanguage(PathLanguage pathLang);
89+
6090
/**
6191
* Specifies an operation to delete an existing JSON or XML fragment.
6292
* @param selectPath the location of the JSON or XML fragment

src/main/java/com/marklogic/client/impl/DocumentMetadataPatchBuilderImpl.java

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.marklogic.client.MarkLogicIOException;
3434
import com.marklogic.client.document.DocumentManager.Metadata;
3535
import com.marklogic.client.document.DocumentMetadataPatchBuilder;
36+
import com.marklogic.client.document.DocumentPatchBuilder.PathLanguage;
3637
import com.marklogic.client.io.DocumentMetadataHandle.Capability;
3738
import com.marklogic.client.io.Format;
3839
import com.marklogic.client.io.StringHandle;
@@ -53,6 +54,7 @@ class DocumentMetadataPatchBuilderImpl
5354
reserved.put("xs", XMLConstants.W3C_XML_SCHEMA_NS_URI);
5455
};
5556

57+
5658
static class XMLOutputSerializer {
5759
private StringWriter writer;
5860
private XMLStreamWriter serializer;
@@ -340,7 +342,9 @@ static class AddCollectionOperation extends PatchOperation {
340342
}
341343
@Override
342344
public void write(JSONStringWriter serializer) {
343-
writeStartInsert(serializer, "$.collections", "last-child", null);
345+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
346+
"$.collections" : "/node()/array-node('collections')";
347+
writeStartInsert(serializer, pathString, "last-child", null);
344348
serializer.writeStartEntry("content");
345349
serializer.writeStringValue(collection);
346350
serializer.writeEndObject();
@@ -364,7 +368,10 @@ static class DeleteCollectionOperation extends PatchOperation {
364368
}
365369
@Override
366370
public void write(JSONStringWriter serializer) {
367-
writeDelete(serializer, "$.collections[*][?(@="+JSONStringWriter.toJSON(collection)+")]", null);
371+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
372+
"$.collections[*][?(@="+JSONStringWriter.toJSON(collection)+")]" :
373+
"/node()/array-node('collections')/node()[.="+JSONStringWriter.toJSON(collection)+"]";
374+
writeDelete(serializer, pathString, null);
368375
}
369376
@Override
370377
public void write(XMLOutputSerializer out) throws Exception {
@@ -386,8 +393,12 @@ static class ReplaceCollectionOperation extends PatchOperation {
386393
}
387394
@Override
388395
public void write(JSONStringWriter serializer) {
396+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
397+
"$.collections[*][?(@="+JSONStringWriter.toJSON(oldCollection)+")]" :
398+
"/node()/array-node('collections')/node()[.="+JSONStringWriter.toJSON(oldCollection)+"]";
399+
389400
writeStartReplace(serializer,
390-
"$.collections[*][?(@="+JSONStringWriter.toJSON(oldCollection)+")]",
401+
pathString,
391402
null
392403
);
393404
serializer.writeStartEntry("content");
@@ -419,7 +430,10 @@ static class AddPermissionOperation extends PatchOperation {
419430
}
420431
@Override
421432
public void write(JSONStringWriter serializer) {
422-
writeStartInsert(serializer, "$.permissions", "last-child", null);
433+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
434+
"$.permissions" :
435+
"/node()/array-node('permissions')";
436+
writeStartInsert(serializer, pathString, "last-child", null);
423437
serializer.writeStartEntry("content");
424438
serializer.writeStartObject();
425439
serializer.writeStartEntry("role-name");
@@ -463,8 +477,13 @@ static class DeletePermissionOperation extends PatchOperation {
463477
}
464478
@Override
465479
public void write(JSONStringWriter serializer) {
480+
481+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
482+
"$.permissions.[*][?(@.role-name="+JSONStringWriter.toJSON(role)+")]":
483+
"/node()/array-node('permissions')/object-node()[role-name = "+JSONStringWriter.toJSON(role)+"]";
484+
466485
writeDelete(serializer,
467-
"$.permissions.[*][?(@.role-name="+JSONStringWriter.toJSON(role)+")]",
486+
pathString,
468487
null
469488
);
470489
}
@@ -488,8 +507,12 @@ static class ReplacePermissionOperation extends PatchOperation {
488507
}
489508
@Override
490509
public void write(JSONStringWriter serializer) {
510+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
511+
"$.permissions.[*][?(@.role-name="+JSONStringWriter.toJSON(oldRole)+")]":
512+
"/node()/array-node('permissions')/object-node()[role-name = "+JSONStringWriter.toJSON(oldRole)+"]";
513+
491514
writeStartReplace(serializer,
492-
"$.permissions.[*][?(@.role-name="+JSONStringWriter.toJSON(oldRole)+")]",
515+
pathString,
493516
null
494517
);
495518
serializer.writeStartEntry("content");
@@ -550,7 +573,11 @@ static class AddPropertyOperation extends PatchOperation {
550573
@Override
551574
public void write(JSONStringWriter serializer) {
552575
// TODO: error if name empty
553-
writeStartInsert(serializer, "$.properties", "last-child", null);
576+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
577+
"$.properties" :
578+
"/node()/node('properties')";
579+
580+
writeStartInsert(serializer, pathString, "last-child", null);
554581
serializer.writeStartEntry("content");
555582
serializer.writeStartObject();
556583
serializer.writeStartEntry(name);
@@ -590,8 +617,12 @@ static class DeletePropertyOperation extends PatchOperation {
590617
@Override
591618
public void write(JSONStringWriter serializer) {
592619
// TODO: error if name empty
620+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
621+
"$.properties.["+JSONStringWriter.toJSON(name)+"]":
622+
"/node()/node('properties')/node()[name(.)="+JSONStringWriter.toJSON(name)+"]";
623+
593624
writeDelete(serializer,
594-
"$.properties.["+JSONStringWriter.toJSON(name)+"]",
625+
pathString,
595626
null
596627
);
597628
}
@@ -627,8 +658,11 @@ static class ReplacePropertyOperation extends PatchOperation {
627658
@Override
628659
public void write(JSONStringWriter serializer) {
629660
// TODO: error if name empty
661+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
662+
"$.properties.["+JSONStringWriter.toJSON(oldName)+"]":
663+
"/node()/node('properties')/node()[name(.)="+JSONStringWriter.toJSON(oldName)+"]";
630664
writeStartReplace(serializer,
631-
"$.properties.["+JSONStringWriter.toJSON(oldName)+"]",
665+
pathString,
632666
null
633667
);
634668
serializer.writeStartEntry("content");
@@ -674,8 +708,11 @@ static class ReplacePropertyApplyOperation extends PatchOperation {
674708
@Override
675709
public void write(JSONStringWriter serializer) {
676710
// TODO: error if name empty
711+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
712+
"$.properties.["+JSONStringWriter.toJSON(name)+"]":
713+
"/node()/node('properties')/node()[name(.)="+JSONStringWriter.toJSON(name)+"]";
677714
writeReplaceApply(serializer,
678-
"$.properties.["+JSONStringWriter.toJSON(name)+"]",
715+
pathString,
679716
null,
680717
call
681718
);
@@ -699,7 +736,11 @@ static class SetQualityOperation extends PatchOperation {
699736
}
700737
@Override
701738
public void write(JSONStringWriter serializer) {
702-
writeStartReplace(serializer, "$.quality", null);
739+
String pathString = serializer.getPathLanguage() == PathLanguage.JSONPATH ?
740+
"$.quality":
741+
"/node()/node('quality')";
742+
743+
writeStartReplace(serializer, pathString, null);
703744
serializer.writeStartEntry("content");
704745
serializer.writeNumberValue(quality);
705746
serializer.writeEndObject();
@@ -742,6 +783,7 @@ public boolean isOnContent() {
742783
protected Format format;
743784
protected Set<Metadata> processedMetadata;
744785
protected boolean onContent = false;
786+
protected PathLanguage pathLang = PathLanguage.XPATH;
745787

746788
DocumentMetadataPatchBuilderImpl(Format format) {
747789
super();
@@ -978,9 +1020,11 @@ public PatchHandle build() throws MarkLogicIOException {
9781020
if (format == Format.JSON) {
9791021
handle.setFormat(format);
9801022

981-
JSONStringWriter writer = new JSONStringWriter();
1023+
JSONStringWriter writer = new JSONStringWriter(this.pathLang);
9821024

9831025
writer.writeStartObject();
1026+
writer.writeStartEntry("pathlang");
1027+
writer.writeStringValue(pathLang.toString());
9841028
writer.writeStartEntry("patch");
9851029
writer.writeStartArray();
9861030

src/main/java/com/marklogic/client/impl/DocumentPatchBuilderImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,4 +278,10 @@ private void onContent() {
278278
onContent = true;
279279
}
280280
}
281+
282+
@Override
283+
public DocumentPatchBuilder pathLanguage(PathLanguage pathLang) {
284+
this.pathLang = pathLang;
285+
return this;
286+
}
281287
}

src/main/java/com/marklogic/client/impl/JSONStringWriter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,22 @@
1717

1818
import java.io.CharArrayWriter;
1919

20+
import com.marklogic.client.document.DocumentPatchBuilder.PathLanguage;
21+
2022
public class JSONStringWriter {
2123
private StringBuilder builder;
2224
private boolean isFirst = false;
25+
private PathLanguage pathLang;
2326

24-
JSONStringWriter() {
27+
JSONStringWriter(PathLanguage pathLang) {
2528
super();
29+
this.pathLang = pathLang;
2630
builder = new StringBuilder();
2731
}
32+
33+
public PathLanguage getPathLanguage() {
34+
return this.pathLang;
35+
}
2836

2937
public void writeStartObject() {
3038
builder.append("{");

src/test/java/com/marklogic/client/test/GenericDocumentTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,4 +420,7 @@ public void testPatch() throws IOException, XpathException, SAXException {
420420

421421
docMgr.delete(docId);
422422
}
423+
424+
423425
}
426+

0 commit comments

Comments
 (0)