1
1
package xmlquery
2
2
3
3
import (
4
+ "bufio"
4
5
"encoding/xml"
5
6
"fmt"
6
7
"html"
8
+ "io"
7
9
"strings"
8
10
)
9
11
@@ -153,32 +155,34 @@ type indentation struct {
153
155
level int
154
156
hasChild bool
155
157
indent string
156
- b * strings. Builder
158
+ w io. Writer
157
159
}
158
160
159
- func newIndentation (indent string , b * strings. Builder ) * indentation {
161
+ func newIndentation (indent string , w io. Writer ) * indentation {
160
162
if indent == "" {
161
163
return nil
162
164
}
163
165
return & indentation {
164
166
indent : indent ,
165
- b : b ,
167
+ w : w ,
166
168
}
167
169
}
168
170
169
171
func (i * indentation ) NewLine () {
170
172
if i == nil {
171
173
return
172
174
}
173
- i . b . WriteString ("\n " )
175
+ io . WriteString (i . w , "\n " )
174
176
}
175
177
176
178
func (i * indentation ) Open () {
177
179
if i == nil {
178
180
return
179
181
}
180
- i .b .WriteString ("\n " )
181
- i .b .WriteString (strings .Repeat (i .indent , i .level ))
182
+
183
+ io .WriteString (i .w , "\n " )
184
+ io .WriteString (i .w , strings .Repeat (i .indent , i .level ))
185
+
182
186
i .level ++
183
187
i .hasChild = false
184
188
}
@@ -189,119 +193,120 @@ func (i *indentation) Close() {
189
193
}
190
194
i .level --
191
195
if i .hasChild {
192
- i . b . WriteString ("\n " )
193
- i . b . WriteString (strings .Repeat (i .indent , i .level ))
196
+ io . WriteString (i . w , "\n " )
197
+ io . WriteString (i . w , strings .Repeat (i .indent , i .level ))
194
198
}
195
199
i .hasChild = true
196
200
}
197
201
198
- func outputXML (b * strings. Builder , n * Node , preserveSpaces bool , config * outputConfiguration , indent * indentation ) {
202
+ func outputXML (w io. Writer , n * Node , preserveSpaces bool , config * outputConfiguration , indent * indentation ) {
199
203
preserveSpaces = calculatePreserveSpaces (n , preserveSpaces )
200
204
switch n .Type {
201
205
case TextNode :
202
- b .WriteString (html .EscapeString (n .sanitizedData (preserveSpaces )))
206
+ io .WriteString (w , html .EscapeString (n .sanitizedData (preserveSpaces )))
203
207
return
204
208
case CharDataNode :
205
- b .WriteString ("<![CDATA[" )
206
- b .WriteString (n .Data )
207
- b .WriteString ("]]>" )
209
+ io .WriteString (w , "<![CDATA[" )
210
+ io .WriteString (w , n .Data )
211
+ io .WriteString (w , "]]>" )
208
212
return
209
213
case CommentNode :
210
214
if ! config .skipComments {
211
- b .WriteString ("<!--" )
212
- b .WriteString (n .Data )
213
- b .WriteString ("-->" )
215
+ io .WriteString (w , "<!--" )
216
+ io .WriteString (w , n .Data )
217
+ io .WriteString (w , "-->" )
214
218
}
215
219
return
216
220
case NotationNode :
217
221
indent .NewLine ()
218
- fmt .Fprintf (b , "<!%s>" , n .Data )
222
+ fmt .Fprintf (w , "<!%s>" , n .Data )
219
223
return
220
224
case DeclarationNode :
221
- b .WriteString ("<?" + n .Data )
225
+ io .WriteString (w , "<?" + n .Data )
222
226
default :
223
227
indent .Open ()
224
228
if n .Prefix == "" {
225
- b .WriteString ("<" + n .Data )
229
+ io .WriteString (w , "<" + n .Data )
226
230
} else {
227
- fmt .Fprintf (b , "<%s:%s" , n .Prefix , n .Data )
231
+ fmt .Fprintf (w , "<%s:%s" , n .Prefix , n .Data )
228
232
}
229
233
}
230
234
231
235
for _ , attr := range n .Attr {
232
236
if attr .Name .Space != "" {
233
- fmt .Fprintf (b , ` %s:%s=` , attr .Name .Space , attr .Name .Local )
237
+ fmt .Fprintf (w , ` %s:%s=` , attr .Name .Space , attr .Name .Local )
234
238
} else {
235
- fmt .Fprintf (b , ` %s=` , attr .Name .Local )
239
+ fmt .Fprintf (w , ` %s=` , attr .Name .Local )
236
240
}
237
- b .WriteByte ('"' )
238
- b .WriteString (html .EscapeString (attr .Value ))
239
- b .WriteByte ('"' )
241
+
242
+ fmt .Fprintf (w , `"%v"` , html .EscapeString (attr .Value ))
240
243
}
241
244
if n .Type == DeclarationNode {
242
- b .WriteString ("?>" )
245
+ io .WriteString (w , "?>" )
243
246
} else {
244
247
if n .FirstChild != nil || ! config .emptyElementTagSupport {
245
- b .WriteString (">" )
248
+ io .WriteString (w , ">" )
246
249
} else {
247
- b .WriteString ("/>" )
250
+ io .WriteString (w , "/>" )
248
251
indent .Close ()
249
252
return
250
253
}
251
254
}
252
255
for child := n .FirstChild ; child != nil ; child = child .NextSibling {
253
- outputXML (b , child , preserveSpaces , config , indent )
256
+ outputXML (w , child , preserveSpaces , config , indent )
254
257
}
255
258
if n .Type != DeclarationNode {
256
259
indent .Close ()
257
260
if n .Prefix == "" {
258
- fmt .Fprintf (b , "</%s>" , n .Data )
261
+ fmt .Fprintf (w , "</%s>" , n .Data )
259
262
} else {
260
- fmt .Fprintf (b , "</%s:%s>" , n .Prefix , n .Data )
263
+ fmt .Fprintf (w , "</%s:%s>" , n .Prefix , n .Data )
261
264
}
262
265
}
263
266
}
264
267
265
268
// OutputXML returns the text that including tags name.
266
269
func (n * Node ) OutputXML (self bool ) string {
267
-
268
- config := & outputConfiguration {
269
- printSelf : true ,
270
- emptyElementTagSupport : false ,
270
+ if self {
271
+ return n .OutputXMLWithOptions (WithOutputSelf ())
271
272
}
272
- preserveSpaces := calculatePreserveSpaces (n , false )
273
- var b strings.Builder
274
- if self && n .Type != DocumentNode {
275
- outputXML (& b , n , preserveSpaces , config , newIndentation (config .useIndentation , & b ))
276
- } else {
277
- for n := n .FirstChild ; n != nil ; n = n .NextSibling {
278
- outputXML (& b , n , preserveSpaces , config , newIndentation (config .useIndentation , & b ))
279
- }
280
- }
281
-
282
- return b .String ()
273
+ return n .OutputXMLWithOptions ()
283
274
}
284
275
285
276
// OutputXMLWithOptions returns the text that including tags name.
286
277
func (n * Node ) OutputXMLWithOptions (opts ... OutputOption ) string {
278
+ var b strings.Builder
279
+ n .WriteWithOptions (& b , opts ... )
280
+ return b .String ()
281
+ }
287
282
283
+ // Write writes xml to given writer.
284
+ func (n * Node ) Write (writer io.Writer , self bool ) {
285
+ if self {
286
+ n .WriteWithOptions (writer , WithOutputSelf ())
287
+ }
288
+ n .WriteWithOptions (writer )
289
+ }
290
+
291
+ // WriteWithOptions writes xml with given options to given writer.
292
+ func (n * Node ) WriteWithOptions (writer io.Writer , opts ... OutputOption ) {
288
293
config := & outputConfiguration {}
289
294
// Set the options
290
295
for _ , opt := range opts {
291
296
opt (config )
292
297
}
293
298
pastPreserveSpaces := config .preserveSpaces
294
299
preserveSpaces := calculatePreserveSpaces (n , pastPreserveSpaces )
295
- var b strings.Builder
300
+ b := bufio .NewWriter (writer )
301
+ defer b .Flush ()
302
+
296
303
if config .printSelf && n .Type != DocumentNode {
297
- outputXML (& b , n , preserveSpaces , config , newIndentation (config .useIndentation , & b ))
304
+ outputXML (b , n , preserveSpaces , config , newIndentation (config .useIndentation , b ))
298
305
} else {
299
306
for n := n .FirstChild ; n != nil ; n = n .NextSibling {
300
- outputXML (& b , n , preserveSpaces , config , newIndentation (config .useIndentation , & b ))
307
+ outputXML (b , n , preserveSpaces , config , newIndentation (config .useIndentation , b ))
301
308
}
302
309
}
303
-
304
- return b .String ()
305
310
}
306
311
307
312
// AddAttr adds a new attribute specified by 'key' and 'val' to a node 'n'.
0 commit comments