@@ -62,9 +62,11 @@ public abstract class Serializer implements Closeable {
6262 * @param uri URI (can be {@code null})
6363 * @param value attribute value (can be {@code null})
6464 */
65- private record Att (byte [] name , byte [] value , byte [] uri ) { }
66- /** Attribute/namespace collector. */
67- private final ArrayList <Att > attributes = new ArrayList <>();
65+ protected record Att (byte [] name , byte [] value , byte [] uri ) { }
66+ /** Attribute collector. */
67+ protected final ArrayList <Att > attributes = new ArrayList <>();
68+ /** Namespace collector. */
69+ protected final ArrayList <Att > namespaces = new ArrayList <>();
6870
6971 /** Static context. */
7072 protected StaticContext sc ;
@@ -270,6 +272,15 @@ protected boolean skipElement(final XNode node) {
270272 protected void attribute (final byte [] name , final byte [] value , final boolean standalone )
271273 throws IOException { }
272274
275+ /**
276+ * Returns the element name (may be overridden to modify names).
277+ * @param name original name
278+ * @return modified name
279+ */
280+ protected QNm elementName (final QNm name ) {
281+ return name ;
282+ }
283+
273284 /**
274285 * Starts an element.
275286 * @param name element name
@@ -278,6 +289,13 @@ protected void attribute(final byte[] name, final byte[] value, final boolean st
278289 @ SuppressWarnings ("unused" )
279290 protected void startOpen (final QNm name ) throws IOException { }
280291
292+ /**
293+ * Adjusts namespaces before serializing namespaces and attributes.
294+ * @param name original element name
295+ */
296+ @ SuppressWarnings ("unused" )
297+ protected void adjustNamespaces (final QNm name ) { }
298+
281299 /**
282300 * Finishes an opening element node.
283301 * @throws IOException I/O exception
@@ -358,8 +376,8 @@ private void addAttribute(final byte[] name, final byte[] value, final byte[] ur
358376 * @param prefix prefix
359377 * @param uri URI
360378 */
361- private void addNamespace (final byte [] prefix , final byte [] uri ) {
362- attributes .add (new Att (prefix , null , uri ));
379+ protected void addNamespace (final byte [] prefix , final byte [] uri ) {
380+ namespaces .add (new Att (prefix , null , uri ));
363381 }
364382
365383 /**
@@ -384,10 +402,10 @@ private void emitAttributes() throws IOException {
384402 */
385403 private void emitNamespaces () throws IOException {
386404 if (canonical ) {
387- attributes .sort ((a , b ) -> compare (a .name , b .name ));
405+ namespaces .sort ((a , b ) -> compare (a .name , b .name ));
388406 }
389- for (final Att att : attributes ) namespace (att .name , att .uri , false );
390- attributes .clear ();
407+ for (final Att att : namespaces ) namespace (att .name , att .uri , false );
408+ namespaces .clear ();
391409 }
392410
393411 /**
@@ -454,9 +472,10 @@ private void node(final DBNode node) throws IOException {
454472 nsUri = data .nspaces .uri (data .uriId (pre , kind ));
455473 }
456474 // open element, serialize namespace declaration if it's new
457- openElement (new QNm (name , nsUri ));
475+ final QNm originalName = new QNm (name , nsUri );
476+ openElement (elementName (originalName ));
458477 if (nsUri == null ) nsUri = EMPTY ;
459- namespace (nsPrefix , nsUri , false );
478+ addNamespace (nsPrefix , nsUri );
460479
461480 // database contains namespaces: add declarations
462481 if (nsExist ) {
@@ -476,7 +495,6 @@ private void node(final DBNode node) throws IOException {
476495 } while (p >= 0 && data .kind (p ) == Data .ELEM );
477496
478497 // reset namespace cache
479- emitNamespaces ();
480498 nsSet .clear ();
481499 }
482500
@@ -488,6 +506,8 @@ private void node(final DBNode node) throws IOException {
488506 addAttribute (n , v , canonical ? data .nspaces .uri (data .uriId (pre , Data .ATTR )) : null );
489507 if (eq (n , XML_SPACE ) && indent ) indent = !eq (v , PRESERVE );
490508 }
509+ adjustNamespaces (originalName );
510+ emitNamespaces ();
491511 emitAttributes ();
492512 parentStack .push (par );
493513 }
@@ -531,7 +551,8 @@ private void node(final FNode node) throws IOException {
531551 closeDoc ();
532552 } else if (skip == 0 || !skipElement (node )) {
533553 // serialize elements (code will never be called for attributes)
534- final QNm name = node .qname ();
554+ final QNm originalName = node .qname ();
555+ final QNm name = elementName (originalName );
535556 openElement (name );
536557
537558 // serialize declared namespaces
@@ -540,7 +561,6 @@ private void node(final FNode node) throws IOException {
540561 for (int p = 0 ; p < ps ; p ++) addNamespace (nsp .name (p ), nsp .value (p ));
541562 // add new or updated namespace
542563 addNamespace (name .prefix (), name .uri ());
543- emitNamespaces ();
544564
545565 // serialize attributes
546566 final boolean i = indent ;
@@ -550,6 +570,8 @@ private void node(final FNode node) throws IOException {
550570 addAttribute (n , v , canonical ? nsUri (prefix (n )) : null );
551571 if (eq (n , XML_SPACE ) && indent ) indent = !eq (v , PRESERVE );
552572 }
573+ adjustNamespaces (originalName );
574+ emitNamespaces ();
553575 emitAttributes ();
554576
555577 // serialize children
0 commit comments