@@ -85,6 +85,52 @@ def asdict(self) -> Dict[str, object]:
8585 return {"op" : self .name , "path" : str (self .path ), "value" : self .value }
8686
8787
88+ class OpAddNe (Op ):
89+ """A non-standard _add if not exists_ operation."""
90+
91+ __slots__ = ("path" , "value" )
92+
93+ name = "add"
94+
95+ def __init__ (self , path : JSONPointer , value : object ) -> None :
96+ self .path = path
97+ self .value = value
98+
99+ def apply (
100+ self , data : Union [MutableSequence [object ], MutableMapping [str , object ]]
101+ ) -> Union [MutableSequence [object ], MutableMapping [str , object ]]:
102+ """Apply this patch operation to _data_."""
103+ parent , obj = self .path .resolve_parent (data )
104+ if parent is None :
105+ # Replace the root object.
106+ # The following op, if any, will raise a JSONPatchError if needed.
107+ return self .value # type: ignore
108+
109+ target = self .path .parts [- 1 ]
110+ if isinstance (parent , MutableSequence ):
111+ if obj is UNDEFINED :
112+ if target == "-" :
113+ parent .append (self .value )
114+ else :
115+ raise JSONPatchError ("index out of range" )
116+ else :
117+ parent .insert (int (target ), self .value )
118+ elif (
119+ isinstance (parent , MutableMapping )
120+ and parent .get (target , UNDEFINED ) == UNDEFINED
121+ ):
122+ parent [target ] = self .value
123+ # else:
124+ # raise JSONPatchError(
125+ # f"unexpected operation on {parent.__class__.__name__!r}"
126+ # )
127+ return data
128+
129+ def asdict (self ) -> Dict [str , object ]:
130+ """Return a dictionary representation of this operation."""
131+ return {"op" : self .name , "path" : str (self .path ), "value" : self .value }
132+
133+
88134class OpRemove (Op ):
89135 """The JSON Patch _remove_ operation."""
90136
@@ -340,6 +386,11 @@ def _build(self, patch: Iterable[Mapping[str, object]]) -> None:
340386 path = self ._op_pointer (operation , "path" , "add" , i ),
341387 value = self ._op_value (operation , "value" , "add" , i ),
342388 )
389+ elif op == "addne" :
390+ self .addne (
391+ path = self ._op_pointer (operation , "path" , "add" , i ),
392+ value = self ._op_value (operation , "value" , "add" , i ),
393+ )
343394 elif op == "remove" :
344395 self .remove (path = self ._op_pointer (operation , "path" , "add" , i ))
345396 elif op == "replace" :
@@ -424,6 +475,22 @@ def add(self: Self, path: Union[str, JSONPointer], value: object) -> Self:
424475 self .ops .append (OpAdd (path = pointer , value = value ))
425476 return self
426477
478+ def addne (self : Self , path : Union [str , JSONPointer ], value : object ) -> Self :
479+ """Append an _addne_ operation to this patch.
480+
481+ Arguments:
482+ path: A string representation of a JSON Pointer, or one that has
483+ already been parsed.
484+ value: The object to add.
485+
486+ Returns:
487+ This `JSONPatch` instance, so we can build a JSON Patch by chaining
488+ calls to JSON Patch operation methods.
489+ """
490+ pointer = self ._ensure_pointer (path )
491+ self .ops .append (OpAddNe (path = pointer , value = value ))
492+ return self
493+
427494 def remove (self : Self , path : Union [str , JSONPointer ]) -> Self :
428495 """Append a _remove_ operation to this patch.
429496
0 commit comments