diff Implab/Xml/XmlNameContext.cs @ 227:8d5de4eb9c2c v2

Reimplemented JsonXmlReader, added support for null values: JSON null values are mapped to empty nodes with 'xsi:nil' attribute set to 'true'
author cin
date Sat, 09 Sep 2017 03:53:13 +0300
parents
children 5f7a3e1d32b9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Xml/XmlNameContext.cs	Sat Sep 09 03:53:13 2017 +0300
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml;
+
+namespace Implab.Xml {
+    public class XmlNameContext {
+        public const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
+        public const string XmlnsPrefix = "xmlns";
+        public const string XmlNamespace = "http://www.w3.org/XML/1998/namespace";
+        public const string XmlPrefix = "xml";
+        public const string XsiNamespace = "http://www.w3.org/2001/XMLSchema-instance";
+        public const string XsiPrefix = "xsi";
+
+        readonly static char[] _qNameDelim = new[] { ':' };
+
+        Dictionary<string, string> m_ns2prefix;
+        Dictionary<string, string> m_prefix2ns;
+        int m_nextPrefix = 1;
+        
+        public XmlNameContext ParentContext { get; private set; }
+
+        public XmlNameContext(XmlNameContext parent) {
+            ParentContext = parent;
+            if (parent == null) {
+                DefinePrefixNoCheck(XmlnsNamespace, XmlnsPrefix);
+                DefinePrefixNoCheck(XmlNamespace, XmlPrefix);
+            } else {
+                m_nextPrefix = parent.m_nextPrefix;
+            }
+        }
+
+        public bool LookupNamespacePrefix(string ns, out string prefix) {
+            if (ns == null)
+                ns = string.Empty;
+
+            prefix = null;
+            for (var ctx = this; ctx != null; ctx = ctx.ParentContext) {
+                if (ctx.m_ns2prefix?.TryGetValue(ns, out prefix) == true) {
+                    if (ctx != this) // cache for the future use
+                        DefinePrefixNoCheck(ns, prefix);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public string CreateNamespacePrefix(string ns) {
+            var prefix = $"p{m_nextPrefix++}";
+            DefinePrefixNoCheck(ns, prefix);
+            return prefix;
+        }
+
+        void DefinePrefixNoCheck(string ns, string prefix) {
+            if (ns == null)
+                ns = string.Empty;
+            if (prefix == null)
+                prefix = string.Empty;
+
+            if (m_ns2prefix == null)
+                m_ns2prefix = new Dictionary<string, string>();
+            m_ns2prefix[ns] = prefix;
+
+            if (m_prefix2ns == null)
+                m_prefix2ns = new Dictionary<string, string>();
+            m_prefix2ns[prefix] = ns;
+        }
+
+        public void DefinePrefix(string ns, string prefix) {
+            // according to https://www.w3.org/TR/xml-names/#ns-decl
+
+            // It MUST NOT be declared . Other prefixes MUST NOT be bound to this namespace name, and it MUST NOT be declared as the default namespace
+            if (ns == XmlnsNamespace)
+                throw new Exception($"Attempt to define xmlns:{prefix}='{ns}'");
+
+            // It MAY, but need not, be declared, and MUST NOT be bound to any other namespace name
+            if (ns == XmlNamespace && prefix != XmlPrefix)
+                throw new Exception($"Attempt to define xmlns:{prefix}='{ns}'");
+
+            // add mapping
+            DefinePrefixNoCheck(ns, prefix);
+        }
+
+        public string ResolvePrefix(string prefix) {
+            if (prefix == null)
+                prefix = string.Empty;
+            string ns = null;
+            for(var ctx = this; ctx != null; ctx = ctx.ParentContext) {
+                if (ctx.m_prefix2ns?.TryGetValue(prefix, out ns) == true) {
+                    if (ctx != this) // cache for the future use
+                        DefinePrefixNoCheck(ns, prefix);
+                    return ns;
+                }
+            }
+            return null;
+        }
+        
+        public XmlQualifiedName Resolve(string name) {
+            Safe.ArgumentNotEmpty(name, nameof(name));
+            var parts = name.Split(_qNameDelim, 2, StringSplitOptions.RemoveEmptyEntries);
+
+            if (parts.Length == 2) {
+                return new XmlQualifiedName(parts[1], ResolvePrefix(parts[0]));
+            } else {
+                return new XmlQualifiedName(parts[0], ResolvePrefix(string.Empty));
+            }
+        }
+    }
+}