Mercurial > pub > ImplabNet
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)); + } + } + } +}