@@ -62,6 +62,8 @@ type partialDoc struct {
6262self * lazyNode
6363keys []string
6464obj map [string ]* lazyNode
65+
66+ opts * ApplyOptions
6567}
6668
6769type partialArray struct {
@@ -92,6 +94,8 @@ type ApplyOptions struct {
9294// EnsurePathExistsOnAdd instructs json-patch to recursively create the missing parts of path on "add" operation.
9395// Default to false.
9496EnsurePathExistsOnAdd bool
97+
98+ EscapeHTML bool
9599}
96100
97101// NewApplyOptions creates a default set of options for calls to ApplyWithOptions.
@@ -101,6 +105,7 @@ func NewApplyOptions() *ApplyOptions {
101105AccumulatedCopySizeLimit : AccumulatedCopySizeLimit ,
102106AllowMissingPathOnRemove : false ,
103107EnsurePathExistsOnAdd : false ,
108+ EscapeHTML : true ,
104109}
105110}
106111
@@ -143,13 +148,21 @@ func (n *partialDoc) TrustMarshalJSON(buf *bytes.Buffer) error {
143148if err := buf .WriteByte ('{' ); err != nil {
144149return err
145150}
151+ escaped := true
152+
153+ // n.opts should always be set, but in case we missed a case,
154+ // guard.
155+ if n .opts != nil {
156+ escaped = n .opts .EscapeHTML
157+ }
158+
146159for i , k := range n .keys {
147160if i > 0 {
148161if err := buf .WriteByte (',' ); err != nil {
149162return err
150163}
151164}
152- key , err := json .Marshal ( k )
165+ key , err := json .MarshalEscaped ( k , escaped )
153166if err != nil {
154167return err
155168}
@@ -159,7 +172,7 @@ func (n *partialDoc) TrustMarshalJSON(buf *bytes.Buffer) error {
159172if err := buf .WriteByte (':' ); err != nil {
160173return err
161174}
162- value , err := json .Marshal (n .obj [k ])
175+ value , err := json .MarshalEscaped (n .obj [k ], escaped )
163176if err != nil {
164177return err
165178}
@@ -200,11 +213,11 @@ func (n *partialArray) RedirectMarshalJSON() (interface{}, error) {
200213return n .nodes , nil
201214}
202215
203- func deepCopy (src * lazyNode ) (* lazyNode , int , error ) {
216+ func deepCopy (src * lazyNode , options * ApplyOptions ) (* lazyNode , int , error ) {
204217if src == nil {
205218return nil , 0 , nil
206219}
207- a , err := json .Marshal (src )
220+ a , err := json .MarshalEscaped (src , options . EscapeHTML )
208221if err != nil {
209222return nil , 0 , err
210223}
@@ -222,7 +235,7 @@ func (n *lazyNode) nextByte() byte {
222235return s [0 ]
223236}
224237
225- func (n * lazyNode ) intoDoc () (* partialDoc , error ) {
238+ func (n * lazyNode ) intoDoc (options * ApplyOptions ) (* partialDoc , error ) {
226239if n .which == eDoc {
227240return n .doc , nil
228241}
@@ -241,6 +254,7 @@ func (n *lazyNode) intoDoc() (*partialDoc, error) {
241254return nil , ErrInvalid
242255}
243256
257+ n .doc .opts = options
244258if err != nil {
245259return nil , err
246260}
@@ -551,7 +565,7 @@ func findObject(pd *container, path string, options *ApplyOptions) (container, s
551565return nil , ""
552566}
553567} else {
554- doc , err = next .intoDoc ()
568+ doc , err = next .intoDoc (options )
555569
556570if err != nil {
557571return nil , ""
@@ -769,6 +783,7 @@ func (p Patch) add(doc *container, op Operation, options *ApplyOptions) error {
769783} else {
770784pd = & partialDoc {
771785self : val ,
786+ opts : options ,
772787}
773788}
774789
@@ -874,7 +889,7 @@ func ensurePathExists(pd *container, path string, options *ApplyOptions) error {
874889newNode := newLazyNode (newRawMessage (rawJSONObject ))
875890
876891doc .add (part , newNode , options )
877- doc , err = newNode .intoDoc ()
892+ doc , err = newNode .intoDoc (options )
878893if err != nil {
879894return err
880895}
@@ -887,7 +902,7 @@ func ensurePathExists(pd *container, path string, options *ApplyOptions) error {
887902return err
888903}
889904} else {
890- doc , err = target .intoDoc ()
905+ doc , err = target .intoDoc (options )
891906
892907if err != nil {
893908return err
@@ -973,6 +988,8 @@ func (p Patch) replace(doc *container, op Operation, options *ApplyOptions) erro
973988if ! val .tryAry () {
974989return errors .Wrapf (err , "replace operation value must be object or array" )
975990}
991+ } else {
992+ val .doc .opts = options
976993}
977994}
978995
@@ -1134,7 +1151,7 @@ func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64, op
11341151return errors .Wrapf (ErrMissing , "copy operation does not apply: doc is missing destination path: %s" , path )
11351152}
11361153
1137- valCopy , sz , err := deepCopy (val )
1154+ valCopy , sz , err := deepCopy (val , options )
11381155if err != nil {
11391156return errors .Wrapf (err , "error while performing deep copy" )
11401157}
@@ -1221,6 +1238,7 @@ func (p Patch) ApplyIndentWithOptions(doc []byte, indent string, options *ApplyO
12211238} else {
12221239pd = & partialDoc {
12231240self : self ,
1241+ opts : options ,
12241242}
12251243}
12261244
@@ -1257,11 +1275,18 @@ func (p Patch) ApplyIndentWithOptions(doc []byte, indent string, options *ApplyO
12571275}
12581276}
12591277
1260- if indent != "" {
1261- return json .MarshalIndent (pd , "" , indent )
1278+ data , err := json .MarshalEscaped (pd , options .EscapeHTML )
1279+ if err != nil {
1280+ return nil , err
1281+ }
1282+
1283+ if indent == "" {
1284+ return data , nil
12621285}
12631286
1264- return json .Marshal (pd )
1287+ var buf bytes.Buffer
1288+ json .Indent (& buf , data , "" , indent )
1289+ return buf .Bytes (), nil
12651290}
12661291
12671292// From http://tools.ietf.org/html/rfc6901#section-4 :
0 commit comments