@@ -55,6 +55,8 @@ public final class Xml {
5555 private static final String EMPTY_ARRAY = "-empty-array" ;
5656 private static final String QUOT = """ ;
5757 private static final String XML_HEADER = "<?xml " ;
58+ private static final String DOCTYPE_TEXT = "!DOCTYPE" ;
59+ private static final String DOCTYPE_HEADER = "<" + DOCTYPE_TEXT + " " ;
5860 private static final java .nio .charset .Charset UTF_8 = java .nio .charset .Charset .forName ("UTF-8" );
5961 private static final java .util .regex .Pattern ATTRS = java .util .regex .Pattern .compile (
6062 "((?:(?!\\ s|=).)*)\\ s*?=\\ s*?[\" ']?((?:(?<=\" )(?:(?<=\\ \\ )\" |[^\" ])*|(?<=')"
@@ -610,7 +612,12 @@ public static void writeXml(Object value, String name, XmlStringBuilder builder,
610612 } else if (value instanceof String ) {
611613 if (((String ) value ).isEmpty ()) {
612614 builder .append ("<" + XmlValue .escapeName (name , namespaces )
613- + (addArray ? ARRAY_TRUE : "" ) + " string=\" true\" />" );
615+ + (addArray ? ARRAY_TRUE : "" ));
616+ if (name .startsWith ("?" )) {
617+ builder .append ("?>" );
618+ } else {
619+ builder .append (" string=\" true\" />" );
620+ }
614621 } else {
615622 builder .append ("<" + XmlValue .escapeName (name , namespaces )
616623 + (addArray ? ARRAY_TRUE : "" ) + (name .startsWith ("?" ) ? " " : ">" ));
@@ -898,17 +905,25 @@ public static String toXml(Map map, XmlStringBuilder.Step identStep) {
898905 }
899906
900907 private static void checkLocalMap (final XmlStringBuilder builder , final Map localMap ) {
901- if (localMap == null || localMap .size () != 1
902- || XmlValue .getMapKey (localMap ).startsWith ("-" )
903- || XmlValue .getMapValue (localMap ) instanceof List ) {
904- if ("root" .equals (XmlValue .getMapKey (localMap ))) {
905- writeArray ((List ) XmlValue .getMapValue (localMap ), builder );
908+ final Map localMap2 ;
909+ if (localMap != null && localMap .containsKey (DOCTYPE_TEXT )) {
910+ localMap2 = (Map ) U .clone (localMap );
911+ localMap2 .remove (DOCTYPE_TEXT );
912+ builder .append (DOCTYPE_HEADER ).append (String .valueOf (localMap .get (DOCTYPE_TEXT ))).append (">" ).newLine ();
913+ } else {
914+ localMap2 = localMap ;
915+ }
916+ if (localMap2 == null || localMap2 .size () != 1
917+ || XmlValue .getMapKey (localMap2 ).startsWith ("-" )
918+ || XmlValue .getMapValue (localMap2 ) instanceof List ) {
919+ if ("root" .equals (XmlValue .getMapKey (localMap2 ))) {
920+ writeArray ((List ) XmlValue .getMapValue (localMap2 ), builder );
906921 } else {
907- XmlObject .writeXml (localMap , getRootName (localMap ), builder , false ,
922+ XmlObject .writeXml (localMap2 , getRootName (localMap2 ), builder , false ,
908923 U .<String >newLinkedHashSet (), false );
909924 }
910925 } else {
911- XmlObject .writeXml (localMap , null , builder , false , U .<String >newLinkedHashSet (), false );
926+ XmlObject .writeXml (localMap2 , null , builder , false , U .<String >newLinkedHashSet (), false );
912927 }
913928 }
914929
@@ -1028,11 +1043,15 @@ private static Object createMap(final org.w3c.dom.Node node,
10281043 value = currentNode .getTextContent ();
10291044 }
10301045 if (TEXT .equals (name ) && node .getChildNodes ().getLength () > 1
1031- && String .valueOf (value ).trim ().isEmpty ()
1032- || currentNode .getNodeType () == org .w3c .dom .Node .DOCUMENT_TYPE_NODE ) {
1046+ && String .valueOf (value ).trim ().isEmpty ()) {
10331047 continue ;
10341048 }
1035- addNodeValue (map , name , value , elementMapper , nodeMapper , uniqueIds , namespaces , fromType );
1049+ if (currentNode .getNodeType () == org .w3c .dom .Node .DOCUMENT_TYPE_NODE ) {
1050+ addNodeValue (map , DOCTYPE_TEXT , getDoctypeValue (source ), elementMapper ,
1051+ nodeMapper , uniqueIds , namespaces , fromType );
1052+ } else {
1053+ addNodeValue (map , name , value , elementMapper , nodeMapper , uniqueIds , namespaces , fromType );
1054+ }
10361055 }
10371056 return checkNumberAndBoolean (map , node .getNodeName ());
10381057 }
@@ -1339,6 +1358,23 @@ private static Map<String, String> getHeaderAttributes(final String xml) {
13391358 return result ;
13401359 }
13411360
1361+ protected static String getDoctypeValue (final String xml ) {
1362+ int startIndex = xml .indexOf (DOCTYPE_HEADER ) + DOCTYPE_HEADER .length ();
1363+ char charToFind = '>' ;
1364+ int endIndexPlus = 0 ;
1365+ for (int endIndex = startIndex ; endIndex < xml .length (); endIndex += 1 ) {
1366+ if (xml .charAt (endIndex ) == '[' ) {
1367+ charToFind = ']' ;
1368+ endIndexPlus = 1 ;
1369+ continue ;
1370+ }
1371+ if (xml .charAt (endIndex ) == charToFind ) {
1372+ return xml .substring (startIndex , endIndex + endIndexPlus );
1373+ }
1374+ }
1375+ return "" ;
1376+ }
1377+
13421378 private static class MyEntityResolver implements org .xml .sax .EntityResolver {
13431379 public org .xml .sax .InputSource resolveEntity (String publicId , String systemId ) {
13441380 return new org .xml .sax .InputSource (new java .io .StringReader ("" ));
0 commit comments