NanoXLSX.Core 3.1.0
Loading...
Searching...
No Matches
XmlElement.cs
1/*
2 * NanoXLSX is a small .NET library to generate and read XLSX (Microsoft Excel 2007 or newer) files in an easy and native way
3 * Copyright Raphael Stoeckli © 2026
4 * This library is licensed under the MIT License.
5 * You find a copy of the license in project folder or on: http://opensource.org/licenses/MIT
6 */
7
8using System.Collections.Generic;
9using System.Linq;
10using System.Xml;
11
12namespace NanoXLSX.Utils.Xml
13{
17 public class XmlElement
18 {
19 private readonly bool hasPrefix;
20 private bool hasNameSpaces;
21 private bool hasDefaultNameSpace;
22 private bool hasAttributes;
23 private bool hasInnerValue;
24 private bool hasChildren;
25 private string innerValue;
26 private string defaultXmlNsUri;
27
31 public string Prefix { get; set; }
35 public string Name { get; private set; }
39 public List<XmlElement> Children { get; private set; }
43 public HashSet<XmlAttribute> Attributes { get; private set; }
47 public Dictionary<string, string> PrefixNameSpaceMap { get; private set; }
48
52 public string InnerValue
53 {
54 get => innerValue;
55 set
56 {
57 if (string.IsNullOrEmpty(value))
58 {
59 innerValue = null;
60 hasInnerValue = false;
61 }
62 else
63 {
64 innerValue = value;
65 hasInnerValue = true;
66 }
67 }
68 }
69
75 internal XmlElement(string name, string prefix)
76 {
77 this.Name = name;
78 this.Prefix = prefix;
79 this.hasPrefix = !string.IsNullOrEmpty(prefix);
80 }
81
89 internal void AddNameSpaceAttribute(string prefix, string rootNameSpace, string uri)
90 {
91 if (string.IsNullOrEmpty(prefix) || string.IsNullOrEmpty(uri))
92 {
93 return;
94 }
95 if (PrefixNameSpaceMap == null)
96 {
97 PrefixNameSpaceMap = new Dictionary<string, string>();
98 }
99 if (!PrefixNameSpaceMap.ContainsKey(prefix))
100 {
101 PrefixNameSpaceMap.Add(prefix, uri);
102 }
103 hasNameSpaces = true;
104 AddAttribute(prefix, uri, rootNameSpace);
105 }
106
111 internal void AddDefaultXmlNameSpace(string defaultXmlNsUri)
112 {
113 this.defaultXmlNsUri = defaultXmlNsUri;
114 hasDefaultNameSpace = true;
115 }
116
123 internal void AddAttribute(string name, string value, string prefix = "")
124 {
125 if (!hasAttributes)
126 {
127 Attributes = new HashSet<XmlAttribute>();
128 hasAttributes = true;
129 }
130 Attributes.Add(XmlAttribute.CreateAttribute(name, value, prefix));
131 }
132
137 internal void AddAttribute(XmlAttribute? nullableAttribute)
138 {
139 if (!nullableAttribute.HasValue)
140 {
141 return;
142 }
143 if (!hasAttributes)
144 {
145 Attributes = new HashSet<XmlAttribute>();
146 hasAttributes = true;
147 }
148 Attributes.Add(nullableAttribute.Value);
149 }
150
155 internal void AddAttributes(IEnumerable<XmlAttribute> attributes)
156 {
157 if (attributes == null || !attributes.Any())
158 {
159 return;
160 }
161 if (!hasAttributes)
162 {
163 Attributes = new HashSet<XmlAttribute>();
164 hasAttributes = true;
165 }
166 foreach (XmlAttribute attribute in attributes)
167 {
168 Attributes.Add(attribute);
169 }
170 }
171
181 internal XmlElement AddChildElementWithAttribute(string name, string attributeName, string attributeValue, string namePrefix = "", string attributePrefix = "")
182 {
183 XmlElement childElement = CreateElementWithAttribute(name, attributeName, attributeValue, namePrefix, attributePrefix);
184 AddChildElement(childElement);
185 return childElement;
186 }
187
195 internal XmlElement AddChildElementWithValue(string name, string innerValue, string prefix = "")
196 {
197 if (string.IsNullOrEmpty(innerValue))
198 {
199 return null; // Omit empty nodes
200 }
201 XmlElement childElement = CreateElement(name, prefix);
202 childElement.InnerValue = innerValue;
203 AddChildElement(childElement);
204 return childElement;
205 }
206
213 internal XmlElement AddChildElement(string name, string prefix = "")
214 {
215 XmlElement childElement = CreateElement(name, prefix);
216 AddChildElement(childElement);
217 return childElement;
218 }
219
224 internal void AddChildElement(XmlElement xmlElement)
225 {
226 if (xmlElement == null)
227 {
228 return;
229 }
230 if (!hasChildren)
231 {
232 Children = new List<XmlElement>();
233 hasChildren = true;
234 }
235 Children.Add(xmlElement);
236 }
237
242 internal void AddChildElements(IEnumerable<XmlElement> xmlElements)
243 {
244 if (xmlElements == null || !xmlElements.Any())
245 {
246 return;
247 }
248 if (!hasChildren)
249 {
250 Children = new List<XmlElement>();
251 hasChildren = true;
252 }
253 Children.AddRange(xmlElements);
254 }
255
260 public XmlDocument TransformToDocument()
261 {
262 XmlDocument doc = new XmlDocument
263 {
264 XmlResolver = null
265 };
266 XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
267 if (hasNameSpaces)
268 {
269 foreach (KeyValuePair<string, string> nameSpace in PrefixNameSpaceMap)
270 {
271 if (nameSpace.Key == "xmlns")
272 {
273 continue;
274 }
275 nsManager.AddNamespace(nameSpace.Key, nameSpace.Value);
276 }
277 }
278 // Create the root element from this instance recursively.
279 System.Xml.XmlElement rootElement = null;
280 if (hasDefaultNameSpace)
281 {
282 rootElement = XmlElement.CreateXmlElement(doc, this, nsManager, defaultXmlNsUri);
283 }
284 else
285 {
286 rootElement = XmlElement.CreateXmlElement(doc, this, nsManager);
287 }
288 doc.AppendChild(rootElement);
289 return doc;
290 }
291
297 internal void WriteTo(XmlWriter writer, string defaultNs = null)
298 {
299 string elementDefaultNs = hasDefaultNameSpace ? defaultXmlNsUri : defaultNs;
300
301 if (hasPrefix)
302 {
303 string nsUri = null;
304 if (PrefixNameSpaceMap != null && PrefixNameSpaceMap.TryGetValue(Prefix, out string prefixNs))
305 {
306 nsUri = prefixNs;
307 }
308 writer.WriteStartElement(Prefix, Name, nsUri);
309 }
310 else
311 {
312 writer.WriteStartElement(Name, elementDefaultNs);
313 }
314
315 if (hasNameSpaces && PrefixNameSpaceMap != null)
316 {
317 foreach (KeyValuePair<string, string> ns in PrefixNameSpaceMap)
318 {
319 if (ns.Key == "xmlns")
320 {
321 continue;
322 }
323 writer.WriteAttributeString("xmlns", ns.Key, null, ns.Value);
324 }
325 }
326
327 if (hasAttributes)
328 {
329 foreach (XmlAttribute attr in Attributes)
330 {
331 if (attr.HasPrefix)
332 {
333 if (attr.Prefix == "xmlns")
334 {
335 continue;
336 }
337 string attrNsUri = null;
338 if (PrefixNameSpaceMap != null && PrefixNameSpaceMap.TryGetValue(attr.Prefix, out string mapped))
339 {
340 attrNsUri = mapped;
341 }
342 writer.WriteAttributeString(attr.Prefix, attr.Name, attrNsUri, attr.Value);
343 }
344 else
345 {
346 int colonIndex = attr.Name.IndexOf(':');
347 if (colonIndex > 0)
348 {
349 // Qualified attribute name encoded as a single string (e.g. "mc:Ignorable").
350 // XmlWriter requires the prefix and local name to be supplied separately.
351 string implicitPrefix = attr.Name.Substring(0, colonIndex);
352 string localName = attr.Name.Substring(colonIndex + 1);
353 string nsUri = null;
354 if (PrefixNameSpaceMap != null && PrefixNameSpaceMap.TryGetValue(implicitPrefix, out string mappedNs))
355 {
356 nsUri = mappedNs;
357 }
358 writer.WriteAttributeString(implicitPrefix, localName, nsUri, attr.Value);
359 }
360 else
361 {
362 writer.WriteAttributeString(attr.Name, attr.Value);
363 }
364 }
365 }
366 }
367
368 if (hasInnerValue)
369 {
370 writer.WriteString(innerValue);
371 }
372
373 if (hasChildren)
374 {
375 foreach (XmlElement child in Children)
376 {
377 child.WriteTo(writer, elementDefaultNs);
378 }
379 }
380
381 writer.WriteEndElement();
382 }
383
389 public IEnumerable<XmlElement> FindChildElementsByName(string name)
390 {
391 if (!hasChildren)
392 {
393 return Enumerable.Empty<XmlElement>();
394 }
395 List<XmlElement> result = new List<XmlElement>();
396 foreach (XmlElement child in Children)
397 {
398 if (child.Name == name)
399 {
400 result.Add(child);
401 }
402 result.AddRange(child.FindChildElementsByName(name));
403 }
404 return result;
405 }
406
413 public IEnumerable<XmlElement> FindChildElementsByNameAndAttribute(string elementName, string attributeName)
414 {
415 return FindChildElementsByNameAndAttribute(elementName, attributeName, null, false);
416 }
417
425 public IEnumerable<XmlElement> FindChildElementsByNameAndAttribute(string elementName, string attributeName, string attributeValue)
426 {
427 return FindChildElementsByNameAndAttribute(elementName, attributeName, attributeValue, true);
428 }
429
438 private IEnumerable<XmlElement> FindChildElementsByNameAndAttribute(string elementName, string attributeName, string attributeValue, bool useValue)
439 {
440 if (!hasChildren)
441 {
442 return Enumerable.Empty<XmlElement>();
443 }
444 List<XmlElement> result = new List<XmlElement>();
445 foreach (XmlElement child in Children)
446 {
447 if (child.Name == elementName && child.hasAttributes)
448 {
449 XmlAttribute? attribute = XmlAttribute.FindAttribute(attributeName, child.Attributes);
450 if (attribute != null)
451 {
452 if (!useValue || (useValue && attribute.Value.Value == attributeValue))
453 {
454 result.Add(child);
455 }
456 }
457 }
458 result.AddRange(child.FindChildElementsByNameAndAttribute(elementName, attributeName, attributeValue, useValue));
459 }
460 return result;
461 }
462
469 internal static XmlElement CreateElement(string name, string prefix = "")
470 {
471 return new XmlElement(name, prefix);
472 }
473
483 internal static XmlElement CreateElementWithAttribute(string name, string attributeName, string attributeValue, string namePrefix = "", string attributePrefix = "")
484 {
485 XmlElement element = new XmlElement(name, namePrefix)
486 {
487 Attributes = new HashSet<XmlAttribute>()
488 };
489 element.Attributes.Add(XmlAttribute.CreateAttribute(attributeName, attributeValue, attributePrefix));
490 element.hasAttributes = true;
491 return element;
492 }
493
502 private static System.Xml.XmlElement CreateXmlElement(XmlDocument doc, XmlElement customElement, XmlNamespaceManager nsManager, string defaultXmlNsUri = null)
503 {
504 System.Xml.XmlElement xmlElem;
505 if (customElement.hasPrefix)
506 {
507 xmlElem = doc.CreateElement(customElement.Prefix, customElement.Name, nsManager.LookupNamespace(customElement.Prefix));
508 }
509 else
510 {
511 xmlElem = doc.CreateElement(customElement.Name, defaultXmlNsUri);
512 }
513
514 // Add attributes
515 if (customElement.hasAttributes)
516 {
517 foreach (var attr in customElement.Attributes)
518 {
519 if (attr.HasPrefix)
520 {
521 System.Xml.XmlAttribute xmlAttr = doc.CreateAttribute(attr.Prefix, attr.Name, nsManager.LookupNamespace(attr.Prefix));
522 xmlAttr.Value = attr.Value;
523 xmlElem.Attributes.Append(xmlAttr);
524 }
525 else
526 {
527 xmlElem.SetAttribute(attr.Name, attr.Value);
528 }
529 }
530 }
531
532 // Set inner text if available
533 if (customElement.hasInnerValue)
534 {
535 xmlElem.InnerText = customElement.InnerValue;
536 }
537
538 // Process children recursively.
539 if (customElement.hasChildren)
540 {
541 foreach (var child in customElement.Children)
542 {
543 System.Xml.XmlElement childXmlElem = XmlElement.CreateXmlElement(doc, child, nsManager, defaultXmlNsUri);
544 xmlElem.AppendChild(childXmlElem);
545 }
546 }
547 return xmlElem;
548 }
549 }
550}
Class representing an internally used XML element / node.
Definition XmlElement.cs:18
string Name
Name of the element (without prefix).
Definition XmlElement.cs:35
HashSet< XmlAttribute > Attributes
List of attributes of this element. If none, the list is null.
Definition XmlElement.cs:43
string Prefix
Prefix of the element. If not defined, the prefix will be an empty string.
Definition XmlElement.cs:31
XmlDocument TransformToDocument()
Transforms this custom XmlElement (and its children) into a standard XmlDocument.
IEnumerable< XmlElement > FindChildElementsByName(string name)
Method to find XML child elements, based of its name. Name space and hierarchy is not considered as e...
Dictionary< string, string > PrefixNameSpaceMap
Map of prefixes and corresponding name space URIs of this element.
Definition XmlElement.cs:47
IEnumerable< XmlElement > FindChildElementsByNameAndAttribute(string elementName, string attributeName, string attributeValue)
Method to find XML child elements, based of its name, an attribute name and value....
string InnerValue
Gets or set the inner value of the element.
Definition XmlElement.cs:53
List< XmlElement > Children
List of child elements. If none, the list is null.
Definition XmlElement.cs:39
IEnumerable< XmlElement > FindChildElementsByNameAndAttribute(string elementName, string attributeName)
Method to find XML child elements, based of its name, an attribute name. Name space and hierarchy is ...