@@ -19,12 +19,7 @@ public class SitemapSerializer : ISitemapSerializer
1919
2020 public SitemapSerializer ( )
2121 {
22- _serializer = CreateSerializer ( ) ;
23- }
24-
25- private static XmlSerializer CreateSerializer ( )
26- {
27- return new XmlSerializer ( typeof ( Sitemap ) ) ;
22+ _serializer = new XmlSerializer ( typeof ( Sitemap ) ) ;
2823 }
2924
3025 public string Serialize ( ISitemap sitemap )
@@ -36,10 +31,19 @@ public string Serialize(ISitemap sitemap)
3631
3732 string xml ;
3833
34+ var settings = new XmlWriterSettings { Indent = true } ;
35+
3936 using ( var writer = new StringWriterUtf8 ( ) )
4037 {
41- _serializer . Serialize ( writer , sitemap ) ;
42-
38+ using ( var xmlWriter = XmlWriter . Create ( writer , settings ) )
39+ {
40+ var namespaces = new XmlSerializerNamespaces ( ) ;
41+ // set default namespace to sitemap protocol
42+ namespaces . Add ( string . Empty , "http://www.sitemaps.org/schemas/sitemap/0.9" ) ;
43+
44+ _serializer . Serialize ( xmlWriter , sitemap , namespaces ) ;
45+ }
46+
4347 xml = writer . ToString ( ) ;
4448 }
4549
@@ -51,78 +55,94 @@ private static string XmlPostProcessing(string xml)
5155 // Post-process generated XML to remove xsi:nil="true" for <changefreq> elements.
5256 // This avoids changing the Url class while ensuring the output conforms to the
5357 // Sitemaps protocol (no nil attributes for optional elements).
54- try
58+
59+ var doc = new XmlDocument ( ) ;
60+ doc . LoadXml ( xml ) ;
61+
62+ var nodes = doc . GetElementsByTagName ( "changefreq" ) ;
63+
64+ const string xsiNs = "http://www.w3.org/2001/XMLSchema-instance" ;
65+
66+ // Ensure root has the sitemap default namespace and remove only the xsi namespace
67+ // declarations that are no longer needed (e.g. xmlns:xsi and xsi:schemaLocation).
68+ var root = doc . DocumentElement ;
69+
70+ const string sitemapNs = "http://www.sitemaps.org/schemas/sitemap/0.9" ;
71+
72+ if ( root is not null )
5573 {
56- var doc = new XmlDocument ( ) ;
57- doc . LoadXml ( xml ) ;
74+ // Ensure default xmlns is present and correct
75+ root . SetAttribute ( "xmlns" , sitemapNs ) ;
5876
59- var nodes = doc . GetElementsByTagName ( "changefreq" ) ;
60-
61- const string xsiNs = "http://www.w3.org/2001/XMLSchema-instance" ;
77+ // Remove xmlns:xsi if present
78+ var xmlnsXsi = root . GetAttributeNode ( "xmlns:xsi" ) ;
6279
63- // Ensure root has the sitemap default namespace and remove only the xsi namespace
64- // declarations that are no longer needed (e.g. xmlns:xsi and xsi:schemaLocation).
65- var root = doc . DocumentElement ;
66-
67- const string sitemapNs = "http://www.sitemaps.org/schemas/sitemap/0.9" ;
80+ if ( xmlnsXsi is not null )
81+ {
82+ root . RemoveAttributeNode ( xmlnsXsi ) ;
83+ }
84+
85+ // Remove xsi:schemaLocation if present
86+ var schemaLoc = root . GetAttributeNode ( "schemaLocation" , xsiNs ) ;
6887
69- if ( root is not null )
88+ if ( schemaLoc is not null )
7089 {
71- // Ensure default xmlns is present and correct
72- root . SetAttribute ( "xmlns" , sitemapNs ) ;
73-
74- // Remove xmlns:xsi if present
75- var xmlnsXsi = root . GetAttributeNode ( "xmlns:xsi" ) ;
76-
77- if ( xmlnsXsi is not null )
78- {
79- root . RemoveAttributeNode ( xmlnsXsi ) ;
80- }
81-
82- // Remove xsi:schemaLocation if present
83- var schemaLoc = root . GetAttributeNode ( "schemaLocation" , xsiNs ) ;
84-
85- if ( schemaLoc is not null )
86- {
87- root . RemoveAttributeNode ( schemaLoc ) ;
88- }
90+ root . RemoveAttributeNode ( schemaLoc ) ;
8991 }
92+ }
93+
94+ // Collect nodes first to avoid modifying the live XmlNodeList during iteration
95+ var list = new List < XmlElement > ( ) ;
9096
91- // Collect nodes first to avoid modifying the live XmlNodeList during iteration
92- var list = new List < XmlElement > ( ) ;
93-
94- foreach ( XmlNode node in nodes )
97+ foreach ( XmlNode node in nodes )
98+ {
99+ if ( node is XmlElement el )
95100 {
96- if ( node is XmlElement el )
97- {
98- list . Add ( el ) ;
99- }
101+ list . Add ( el ) ;
100102 }
103+ }
101104
102- foreach ( var el in list )
105+ foreach ( var el in list )
106+ {
107+ var attr = el . GetAttributeNode ( "nil" , xsiNs ) ;
108+
109+ if ( attr != null && string . Equals ( attr . Value , "true" , StringComparison . OrdinalIgnoreCase ) )
103110 {
104- var attr = el . GetAttributeNode ( "nil" , xsiNs ) ;
105-
106- if ( attr != null && string . Equals ( attr . Value , "true" , StringComparison . OrdinalIgnoreCase ) )
107- {
108- // remove the entire element to avoid deserializing an empty value into the enum
109- var parent = el . ParentNode ;
110-
111- parent ? . RemoveChild ( el ) ;
112- }
111+ // remove the entire element to avoid deserializing an empty value into the enum
112+ var parent = el . ParentNode ;
113+
114+ parent ? . RemoveChild ( el ) ;
113115 }
116+ }
117+
118+ // Normalize priority values: ensure integer values serialize as one decimal (e.g. 1 -> 1.0)
119+ var priorityNodes = doc . GetElementsByTagName ( "priority" ) ;
120+ var priorityList = new List < XmlElement > ( ) ;
114121
115- using var writer = new StringWriterUtf8 ( ) ;
116-
117- doc . Save ( writer ) ;
118-
119- return writer . ToString ( ) ;
122+ foreach ( XmlNode node in priorityNodes )
123+ {
124+ if ( node is XmlElement el )
125+ {
126+ priorityList . Add ( el ) ;
127+ }
120128 }
121- catch
129+
130+ foreach ( var p in priorityList )
122131 {
123- // If anything goes wrong in post-processing, fall back to the original XML
124- return xml ;
132+ var text = p . InnerText ? . Trim ( ) ?? string . Empty ;
133+
134+ // If the value is an integer (no decimal point) and a valid number, append .0
135+ if ( ! string . IsNullOrEmpty ( text ) && ! text . Contains ( "." ) && double . TryParse ( text , out _ ) )
136+ {
137+ p . InnerText = text + ".0" ;
138+ }
125139 }
140+
141+ using var writer = new StringWriterUtf8 ( ) ;
142+
143+ doc . Save ( writer ) ;
144+
145+ return writer . ToString ( ) ;
126146 }
127147
128148 public Sitemap Deserialize ( string xml )
@@ -133,7 +153,7 @@ public Sitemap Deserialize(string xml)
133153 }
134154
135155 using TextReader textReader = new StringReader ( xml ) ;
136-
156+
137157 var obj = _serializer . Deserialize ( textReader ) ;
138158
139159 if ( obj is null )
0 commit comments