2021-03-29 14:54:12 +08:00
#region License
// Copyright (c) 2007 James Newton-King
/ /
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
/ /
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
/ /
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
# endregion
#if (HAVE_XML_DOCUMENT || HAVE_XLINQ)
#if HAVE_BIG_INTEGER
using System.Numerics ;
# endif
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Xml ;
using LC.Newtonsoft.Json.Serialization ;
#if HAVE_XLINQ
using System.Xml.Linq ;
# endif
using LC.Newtonsoft.Json.Utilities ;
using System.Runtime.CompilerServices ;
namespace LC.Newtonsoft.Json.Converters
{
#region XmlNodeWrappers
#if HAVE_XML_DOCUMENT
internal class XmlDocumentWrapper : XmlNodeWrapper , IXmlDocument
{
private readonly XmlDocument _document ;
public XmlDocumentWrapper ( XmlDocument document )
: base ( document )
{
_document = document ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateComment ( string data )
2021-03-29 14:54:12 +08:00
{
return new XmlNodeWrapper ( _document . CreateComment ( data ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateTextNode ( string text )
2021-03-29 14:54:12 +08:00
{
return new XmlNodeWrapper ( _document . CreateTextNode ( text ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateCDataSection ( string data )
2021-03-29 14:54:12 +08:00
{
return new XmlNodeWrapper ( _document . CreateCDataSection ( data ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateWhitespace ( string text )
2021-03-29 14:54:12 +08:00
{
return new XmlNodeWrapper ( _document . CreateWhitespace ( text ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateSignificantWhitespace ( string text )
2021-03-29 14:54:12 +08:00
{
return new XmlNodeWrapper ( _document . CreateSignificantWhitespace ( text ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateXmlDeclaration ( string version , string encoding , string standalone )
2021-03-29 14:54:12 +08:00
{
return new XmlDeclarationWrapper ( _document . CreateXmlDeclaration ( version , encoding , standalone ) ) ;
}
#if HAVE_XML_DOCUMENT_TYPE
2021-03-30 10:54:25 +08:00
public IXmlNode CreateXmlDocumentType ( string name , string publicId , string systemId , string internalSubset )
2021-03-29 14:54:12 +08:00
{
return new XmlDocumentTypeWrapper ( _document . CreateDocumentType ( name , publicId , systemId , null ) ) ;
}
# endif
2021-03-30 10:54:25 +08:00
public IXmlNode CreateProcessingInstruction ( string target , string data )
2021-03-29 14:54:12 +08:00
{
return new XmlNodeWrapper ( _document . CreateProcessingInstruction ( target , data ) ) ;
}
public IXmlElement CreateElement ( string elementName )
{
return new XmlElementWrapper ( _document . CreateElement ( elementName ) ) ;
}
public IXmlElement CreateElement ( string qualifiedName , string namespaceUri )
{
return new XmlElementWrapper ( _document . CreateElement ( qualifiedName , namespaceUri ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateAttribute ( string name , string value )
2021-03-29 14:54:12 +08:00
{
XmlNodeWrapper attribute = new XmlNodeWrapper ( _document . CreateAttribute ( name ) ) ;
attribute . Value = value ;
return attribute ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateAttribute ( string qualifiedName , string namespaceUri , string value )
2021-03-29 14:54:12 +08:00
{
XmlNodeWrapper attribute = new XmlNodeWrapper ( _document . CreateAttribute ( qualifiedName , namespaceUri ) ) ;
attribute . Value = value ;
return attribute ;
}
2021-03-30 10:54:25 +08:00
public IXmlElement DocumentElement
2021-03-29 14:54:12 +08:00
{
get
{
if ( _document . DocumentElement = = null )
{
return null ;
}
return new XmlElementWrapper ( _document . DocumentElement ) ;
}
}
}
internal class XmlElementWrapper : XmlNodeWrapper , IXmlElement
{
private readonly XmlElement _element ;
public XmlElementWrapper ( XmlElement element )
: base ( element )
{
_element = element ;
}
public void SetAttributeNode ( IXmlNode attribute )
{
XmlNodeWrapper xmlAttributeWrapper = ( XmlNodeWrapper ) attribute ;
2021-03-30 10:54:25 +08:00
_element . SetAttributeNode ( ( XmlAttribute ) xmlAttributeWrapper . WrappedNode ) ;
2021-03-29 14:54:12 +08:00
}
public string GetPrefixOfNamespace ( string namespaceUri )
{
return _element . GetPrefixOfNamespace ( namespaceUri ) ;
}
public bool IsEmpty = > _element . IsEmpty ;
}
internal class XmlDeclarationWrapper : XmlNodeWrapper , IXmlDeclaration
{
private readonly XmlDeclaration _declaration ;
public XmlDeclarationWrapper ( XmlDeclaration declaration )
: base ( declaration )
{
_declaration = declaration ;
}
public string Version = > _declaration . Version ;
public string Encoding
{
get = > _declaration . Encoding ;
set = > _declaration . Encoding = value ;
}
public string Standalone
{
get = > _declaration . Standalone ;
set = > _declaration . Standalone = value ;
}
}
#if HAVE_XML_DOCUMENT_TYPE
internal class XmlDocumentTypeWrapper : XmlNodeWrapper , IXmlDocumentType
{
private readonly XmlDocumentType _documentType ;
public XmlDocumentTypeWrapper ( XmlDocumentType documentType )
: base ( documentType )
{
_documentType = documentType ;
}
public string Name = > _documentType . Name ;
public string System = > _documentType . SystemId ;
public string Public = > _documentType . PublicId ;
public string InternalSubset = > _documentType . InternalSubset ;
2021-03-30 10:54:25 +08:00
public override string LocalName = > "DOCTYPE" ;
2021-03-29 14:54:12 +08:00
}
# endif
internal class XmlNodeWrapper : IXmlNode
{
private readonly XmlNode _node ;
2021-03-30 10:54:25 +08:00
private List < IXmlNode > _childNodes ;
private List < IXmlNode > _attributes ;
2021-03-29 14:54:12 +08:00
public XmlNodeWrapper ( XmlNode node )
{
_node = node ;
}
2021-03-30 10:54:25 +08:00
public object WrappedNode = > _node ;
2021-03-29 14:54:12 +08:00
public XmlNodeType NodeType = > _node . NodeType ;
2021-03-30 10:54:25 +08:00
public virtual string LocalName = > _node . LocalName ;
2021-03-29 14:54:12 +08:00
public List < IXmlNode > ChildNodes
{
get
{
// childnodes is read multiple times
// cache results to prevent multiple reads which kills perf in large documents
if ( _childNodes = = null )
{
if ( ! _node . HasChildNodes )
{
_childNodes = XmlNodeConverter . EmptyChildNodes ;
}
else
{
_childNodes = new List < IXmlNode > ( _node . ChildNodes . Count ) ;
foreach ( XmlNode childNode in _node . ChildNodes )
{
_childNodes . Add ( WrapNode ( childNode ) ) ;
}
}
}
return _childNodes ;
}
}
protected virtual bool HasChildNodes = > _node . HasChildNodes ;
internal static IXmlNode WrapNode ( XmlNode node )
{
switch ( node . NodeType )
{
case XmlNodeType . Element :
return new XmlElementWrapper ( ( XmlElement ) node ) ;
case XmlNodeType . XmlDeclaration :
return new XmlDeclarationWrapper ( ( XmlDeclaration ) node ) ;
#if HAVE_XML_DOCUMENT_TYPE
case XmlNodeType . DocumentType :
return new XmlDocumentTypeWrapper ( ( XmlDocumentType ) node ) ;
# endif
default :
return new XmlNodeWrapper ( node ) ;
}
}
public List < IXmlNode > Attributes
{
get
{
// attributes is read multiple times
// cache results to prevent multiple reads which kills perf in large documents
if ( _attributes = = null )
{
if ( ! HasAttributes )
{
_attributes = XmlNodeConverter . EmptyChildNodes ;
}
else
{
_attributes = new List < IXmlNode > ( _node . Attributes . Count ) ;
foreach ( XmlAttribute attribute in _node . Attributes )
{
_attributes . Add ( WrapNode ( attribute ) ) ;
}
}
}
return _attributes ;
}
}
private bool HasAttributes
{
get
{
if ( _node is XmlElement element )
{
return element . HasAttributes ;
}
return _node . Attributes ? . Count > 0 ;
}
}
2021-03-30 10:54:25 +08:00
public IXmlNode ParentNode
2021-03-29 14:54:12 +08:00
{
get
{
XmlNode node = _node is XmlAttribute attribute ? attribute . OwnerElement : _node . ParentNode ;
if ( node = = null )
{
return null ;
}
return WrapNode ( node ) ;
}
}
2021-03-30 10:54:25 +08:00
public string Value
2021-03-29 14:54:12 +08:00
{
get = > _node . Value ;
set = > _node . Value = value ;
}
public IXmlNode AppendChild ( IXmlNode newChild )
{
XmlNodeWrapper xmlNodeWrapper = ( XmlNodeWrapper ) newChild ;
_node . AppendChild ( xmlNodeWrapper . _node ) ;
_childNodes = null ;
_attributes = null ;
return newChild ;
}
2021-03-30 10:54:25 +08:00
public string NamespaceUri = > _node . NamespaceURI ;
2021-03-29 14:54:12 +08:00
}
# endif
# endregion
#region Interfaces
internal interface IXmlDocument : IXmlNode
{
2021-03-30 10:54:25 +08:00
IXmlNode CreateComment ( string text ) ;
IXmlNode CreateTextNode ( string text ) ;
IXmlNode CreateCDataSection ( string data ) ;
IXmlNode CreateWhitespace ( string text ) ;
IXmlNode CreateSignificantWhitespace ( string text ) ;
IXmlNode CreateXmlDeclaration ( string version , string encoding , string standalone ) ;
2021-03-29 14:54:12 +08:00
#if HAVE_XML_DOCUMENT_TYPE
2021-03-30 10:54:25 +08:00
IXmlNode CreateXmlDocumentType ( string name , string publicId , string systemId , string internalSubset ) ;
2021-03-29 14:54:12 +08:00
# endif
2021-03-30 10:54:25 +08:00
IXmlNode CreateProcessingInstruction ( string target , string data ) ;
2021-03-29 14:54:12 +08:00
IXmlElement CreateElement ( string elementName ) ;
IXmlElement CreateElement ( string qualifiedName , string namespaceUri ) ;
2021-03-30 10:54:25 +08:00
IXmlNode CreateAttribute ( string name , string value ) ;
IXmlNode CreateAttribute ( string qualifiedName , string namespaceUri , string value ) ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
IXmlElement DocumentElement { get ; }
2021-03-29 14:54:12 +08:00
}
internal interface IXmlDeclaration : IXmlNode
{
string Version { get ; }
string Encoding { get ; set ; }
string Standalone { get ; set ; }
}
internal interface IXmlDocumentType : IXmlNode
{
string Name { get ; }
string System { get ; }
string Public { get ; }
string InternalSubset { get ; }
}
internal interface IXmlElement : IXmlNode
{
void SetAttributeNode ( IXmlNode attribute ) ;
string GetPrefixOfNamespace ( string namespaceUri ) ;
bool IsEmpty { get ; }
}
internal interface IXmlNode
{
XmlNodeType NodeType { get ; }
2021-03-30 10:54:25 +08:00
string LocalName { get ; }
2021-03-29 14:54:12 +08:00
List < IXmlNode > ChildNodes { get ; }
List < IXmlNode > Attributes { get ; }
2021-03-30 10:54:25 +08:00
IXmlNode ParentNode { get ; }
string Value { get ; set ; }
2021-03-29 14:54:12 +08:00
IXmlNode AppendChild ( IXmlNode newChild ) ;
2021-03-30 10:54:25 +08:00
string NamespaceUri { get ; }
object WrappedNode { get ; }
2021-03-29 14:54:12 +08:00
}
# endregion
#region XNodeWrappers
#if HAVE_XLINQ
internal class XDeclarationWrapper : XObjectWrapper , IXmlDeclaration
{
internal XDeclaration Declaration { get ; }
public XDeclarationWrapper ( XDeclaration declaration )
: base ( null )
{
Declaration = declaration ;
}
public override XmlNodeType NodeType = > XmlNodeType . XmlDeclaration ;
public string Version = > Declaration . Version ;
public string Encoding
{
get = > Declaration . Encoding ;
set = > Declaration . Encoding = value ;
}
public string Standalone
{
get = > Declaration . Standalone ;
set = > Declaration . Standalone = value ;
}
}
internal class XDocumentTypeWrapper : XObjectWrapper , IXmlDocumentType
{
private readonly XDocumentType _documentType ;
public XDocumentTypeWrapper ( XDocumentType documentType )
: base ( documentType )
{
_documentType = documentType ;
}
public string Name = > _documentType . Name ;
public string System = > _documentType . SystemId ;
public string Public = > _documentType . PublicId ;
public string InternalSubset = > _documentType . InternalSubset ;
2021-03-30 10:54:25 +08:00
public override string LocalName = > "DOCTYPE" ;
2021-03-29 14:54:12 +08:00
}
internal class XDocumentWrapper : XContainerWrapper , IXmlDocument
{
2021-03-30 10:54:25 +08:00
private XDocument Document = > ( XDocument ) WrappedNode ;
2021-03-29 14:54:12 +08:00
public XDocumentWrapper ( XDocument document )
: base ( document )
{
}
public override List < IXmlNode > ChildNodes
{
get
{
List < IXmlNode > childNodes = base . ChildNodes ;
if ( Document . Declaration ! = null & & ( childNodes . Count = = 0 | | childNodes [ 0 ] . NodeType ! = XmlNodeType . XmlDeclaration ) )
{
childNodes . Insert ( 0 , new XDeclarationWrapper ( Document . Declaration ) ) ;
}
return childNodes ;
}
}
protected override bool HasChildNodes
{
get
{
if ( base . HasChildNodes )
{
return true ;
}
return Document . Declaration ! = null ;
}
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateComment ( string text )
2021-03-29 14:54:12 +08:00
{
return new XObjectWrapper ( new XComment ( text ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateTextNode ( string text )
2021-03-29 14:54:12 +08:00
{
return new XObjectWrapper ( new XText ( text ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateCDataSection ( string data )
2021-03-29 14:54:12 +08:00
{
return new XObjectWrapper ( new XCData ( data ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateWhitespace ( string text )
2021-03-29 14:54:12 +08:00
{
return new XObjectWrapper ( new XText ( text ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateSignificantWhitespace ( string text )
2021-03-29 14:54:12 +08:00
{
return new XObjectWrapper ( new XText ( text ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateXmlDeclaration ( string version , string encoding , string standalone )
2021-03-29 14:54:12 +08:00
{
return new XDeclarationWrapper ( new XDeclaration ( version , encoding , standalone ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateXmlDocumentType ( string name , string publicId , string systemId , string internalSubset )
2021-03-29 14:54:12 +08:00
{
return new XDocumentTypeWrapper ( new XDocumentType ( name , publicId , systemId , internalSubset ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateProcessingInstruction ( string target , string data )
2021-03-29 14:54:12 +08:00
{
return new XProcessingInstructionWrapper ( new XProcessingInstruction ( target , data ) ) ;
}
public IXmlElement CreateElement ( string elementName )
{
return new XElementWrapper ( new XElement ( elementName ) ) ;
}
public IXmlElement CreateElement ( string qualifiedName , string namespaceUri )
{
string localName = MiscellaneousUtils . GetLocalName ( qualifiedName ) ;
return new XElementWrapper ( new XElement ( XName . Get ( localName , namespaceUri ) ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateAttribute ( string name , string value )
2021-03-29 14:54:12 +08:00
{
return new XAttributeWrapper ( new XAttribute ( name , value ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlNode CreateAttribute ( string qualifiedName , string namespaceUri , string value )
2021-03-29 14:54:12 +08:00
{
string localName = MiscellaneousUtils . GetLocalName ( qualifiedName ) ;
return new XAttributeWrapper ( new XAttribute ( XName . Get ( localName , namespaceUri ) , value ) ) ;
}
2021-03-30 10:54:25 +08:00
public IXmlElement DocumentElement
2021-03-29 14:54:12 +08:00
{
get
{
if ( Document . Root = = null )
{
return null ;
}
return new XElementWrapper ( Document . Root ) ;
}
}
public override IXmlNode AppendChild ( IXmlNode newChild )
{
if ( newChild is XDeclarationWrapper declarationWrapper )
{
Document . Declaration = declarationWrapper . Declaration ;
return declarationWrapper ;
}
else
{
return base . AppendChild ( newChild ) ;
}
}
}
internal class XTextWrapper : XObjectWrapper
{
2021-03-30 10:54:25 +08:00
private XText Text = > ( XText ) WrappedNode ;
2021-03-29 14:54:12 +08:00
public XTextWrapper ( XText text )
: base ( text )
{
}
2021-03-30 10:54:25 +08:00
public override string Value
2021-03-29 14:54:12 +08:00
{
get = > Text . Value ;
set = > Text . Value = value ;
}
2021-03-30 10:54:25 +08:00
public override IXmlNode ParentNode
2021-03-29 14:54:12 +08:00
{
get
{
if ( Text . Parent = = null )
{
return null ;
}
return XContainerWrapper . WrapNode ( Text . Parent ) ;
}
}
}
internal class XCommentWrapper : XObjectWrapper
{
2021-03-30 10:54:25 +08:00
private XComment Text = > ( XComment ) WrappedNode ;
2021-03-29 14:54:12 +08:00
public XCommentWrapper ( XComment text )
: base ( text )
{
}
2021-03-30 10:54:25 +08:00
public override string Value
2021-03-29 14:54:12 +08:00
{
get = > Text . Value ;
set = > Text . Value = value ;
}
2021-03-30 10:54:25 +08:00
public override IXmlNode ParentNode
2021-03-29 14:54:12 +08:00
{
get
{
if ( Text . Parent = = null )
{
return null ;
}
return XContainerWrapper . WrapNode ( Text . Parent ) ;
}
}
}
internal class XProcessingInstructionWrapper : XObjectWrapper
{
2021-03-30 10:54:25 +08:00
private XProcessingInstruction ProcessingInstruction = > ( XProcessingInstruction ) WrappedNode ;
2021-03-29 14:54:12 +08:00
public XProcessingInstructionWrapper ( XProcessingInstruction processingInstruction )
: base ( processingInstruction )
{
}
2021-03-30 10:54:25 +08:00
public override string LocalName = > ProcessingInstruction . Target ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
public override string Value
2021-03-29 14:54:12 +08:00
{
get = > ProcessingInstruction . Data ;
set = > ProcessingInstruction . Data = value ;
}
}
internal class XContainerWrapper : XObjectWrapper
{
2021-03-30 10:54:25 +08:00
private List < IXmlNode > _childNodes ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
private XContainer Container = > ( XContainer ) WrappedNode ;
2021-03-29 14:54:12 +08:00
public XContainerWrapper ( XContainer container )
: base ( container )
{
}
public override List < IXmlNode > ChildNodes
{
get
{
// childnodes is read multiple times
// cache results to prevent multiple reads which kills perf in large documents
if ( _childNodes = = null )
{
if ( ! HasChildNodes )
{
_childNodes = XmlNodeConverter . EmptyChildNodes ;
}
else
{
_childNodes = new List < IXmlNode > ( ) ;
foreach ( XNode node in Container . Nodes ( ) )
{
_childNodes . Add ( WrapNode ( node ) ) ;
}
}
}
return _childNodes ;
}
}
protected virtual bool HasChildNodes = > Container . LastNode ! = null ;
2021-03-30 10:54:25 +08:00
public override IXmlNode ParentNode
2021-03-29 14:54:12 +08:00
{
get
{
if ( Container . Parent = = null )
{
return null ;
}
return WrapNode ( Container . Parent ) ;
}
}
internal static IXmlNode WrapNode ( XObject node )
{
if ( node is XDocument document )
{
return new XDocumentWrapper ( document ) ;
}
if ( node is XElement element )
{
return new XElementWrapper ( element ) ;
}
if ( node is XContainer container )
{
return new XContainerWrapper ( container ) ;
}
if ( node is XProcessingInstruction pi )
{
return new XProcessingInstructionWrapper ( pi ) ;
}
if ( node is XText text )
{
return new XTextWrapper ( text ) ;
}
if ( node is XComment comment )
{
return new XCommentWrapper ( comment ) ;
}
if ( node is XAttribute attribute )
{
return new XAttributeWrapper ( attribute ) ;
}
if ( node is XDocumentType type )
{
return new XDocumentTypeWrapper ( type ) ;
}
return new XObjectWrapper ( node ) ;
}
public override IXmlNode AppendChild ( IXmlNode newChild )
{
Container . Add ( newChild . WrappedNode ) ;
_childNodes = null ;
return newChild ;
}
}
internal class XObjectWrapper : IXmlNode
{
2021-03-30 10:54:25 +08:00
private readonly XObject _xmlObject ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
public XObjectWrapper ( XObject xmlObject )
2021-03-29 14:54:12 +08:00
{
_xmlObject = xmlObject ;
}
2021-03-30 10:54:25 +08:00
public object WrappedNode = > _xmlObject ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
public virtual XmlNodeType NodeType = > _xmlObject . NodeType ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
public virtual string LocalName = > null ;
2021-03-29 14:54:12 +08:00
public virtual List < IXmlNode > ChildNodes = > XmlNodeConverter . EmptyChildNodes ;
public virtual List < IXmlNode > Attributes = > XmlNodeConverter . EmptyChildNodes ;
2021-03-30 10:54:25 +08:00
public virtual IXmlNode ParentNode = > null ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
public virtual string Value
2021-03-29 14:54:12 +08:00
{
get = > null ;
set = > throw new InvalidOperationException ( ) ;
}
public virtual IXmlNode AppendChild ( IXmlNode newChild )
{
throw new InvalidOperationException ( ) ;
}
2021-03-30 10:54:25 +08:00
public virtual string NamespaceUri = > null ;
2021-03-29 14:54:12 +08:00
}
internal class XAttributeWrapper : XObjectWrapper
{
2021-03-30 10:54:25 +08:00
private XAttribute Attribute = > ( XAttribute ) WrappedNode ;
2021-03-29 14:54:12 +08:00
public XAttributeWrapper ( XAttribute attribute )
: base ( attribute )
{
}
2021-03-30 10:54:25 +08:00
public override string Value
2021-03-29 14:54:12 +08:00
{
get = > Attribute . Value ;
set = > Attribute . Value = value ;
}
2021-03-30 10:54:25 +08:00
public override string LocalName = > Attribute . Name . LocalName ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
public override string NamespaceUri = > Attribute . Name . NamespaceName ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
public override IXmlNode ParentNode
2021-03-29 14:54:12 +08:00
{
get
{
if ( Attribute . Parent = = null )
{
return null ;
}
return XContainerWrapper . WrapNode ( Attribute . Parent ) ;
}
}
}
internal class XElementWrapper : XContainerWrapper , IXmlElement
{
2021-03-30 10:54:25 +08:00
private List < IXmlNode > _attributes ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
private XElement Element = > ( XElement ) WrappedNode ;
2021-03-29 14:54:12 +08:00
public XElementWrapper ( XElement element )
: base ( element )
{
}
public void SetAttributeNode ( IXmlNode attribute )
{
XObjectWrapper wrapper = ( XObjectWrapper ) attribute ;
Element . Add ( wrapper . WrappedNode ) ;
_attributes = null ;
}
public override List < IXmlNode > Attributes
{
get
{
// attributes is read multiple times
// cache results to prevent multiple reads which kills perf in large documents
if ( _attributes = = null )
{
2021-03-30 10:54:25 +08:00
if ( ! Element . HasAttributes & & ! HasImplicitNamespaceAttribute ( NamespaceUri ) )
2021-03-29 14:54:12 +08:00
{
_attributes = XmlNodeConverter . EmptyChildNodes ;
}
else
{
_attributes = new List < IXmlNode > ( ) ;
foreach ( XAttribute attribute in Element . Attributes ( ) )
{
_attributes . Add ( new XAttributeWrapper ( attribute ) ) ;
}
// ensure elements created with a namespace but no namespace attribute are converted correctly
// e.g. new XElement("{http://example.com}MyElement");
2021-03-30 10:54:25 +08:00
string namespaceUri = NamespaceUri ;
2021-03-29 14:54:12 +08:00
if ( HasImplicitNamespaceAttribute ( namespaceUri ) )
{
_attributes . Insert ( 0 , new XAttributeWrapper ( new XAttribute ( "xmlns" , namespaceUri ) ) ) ;
}
}
}
return _attributes ;
}
}
private bool HasImplicitNamespaceAttribute ( string namespaceUri )
{
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( namespaceUri ) & & namespaceUri ! = ParentNode ? . NamespaceUri )
2021-03-29 14:54:12 +08:00
{
2021-03-30 10:54:25 +08:00
if ( string . IsNullOrEmpty ( GetPrefixOfNamespace ( namespaceUri ) ) )
2021-03-29 14:54:12 +08:00
{
bool namespaceDeclared = false ;
if ( Element . HasAttributes )
{
foreach ( XAttribute attribute in Element . Attributes ( ) )
{
2021-03-30 10:54:25 +08:00
if ( attribute . Name . LocalName = = "xmlns" & & string . IsNullOrEmpty ( attribute . Name . NamespaceName ) & & attribute . Value = = namespaceUri )
2021-03-29 14:54:12 +08:00
{
namespaceDeclared = true ;
}
}
}
if ( ! namespaceDeclared )
{
return true ;
}
}
}
return false ;
}
public override IXmlNode AppendChild ( IXmlNode newChild )
{
IXmlNode result = base . AppendChild ( newChild ) ;
_attributes = null ;
return result ;
}
2021-03-30 10:54:25 +08:00
public override string Value
2021-03-29 14:54:12 +08:00
{
get = > Element . Value ;
set = > Element . Value = value ;
}
2021-03-30 10:54:25 +08:00
public override string LocalName = > Element . Name . LocalName ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
public override string NamespaceUri = > Element . Name . NamespaceName ;
2021-03-29 14:54:12 +08:00
public string GetPrefixOfNamespace ( string namespaceUri )
{
return Element . GetPrefixOfNamespace ( namespaceUri ) ;
}
public bool IsEmpty = > Element . IsEmpty ;
}
# endif
# endregion
/// <summary>
/// Converts XML to and from JSON.
/// </summary>
public class XmlNodeConverter : JsonConverter
{
internal static readonly List < IXmlNode > EmptyChildNodes = new List < IXmlNode > ( ) ;
private const string TextName = "#text" ;
private const string CommentName = "#comment" ;
private const string CDataName = "#cdata-section" ;
private const string WhitespaceName = "#whitespace" ;
private const string SignificantWhitespaceName = "#significant-whitespace" ;
private const string DeclarationName = "?xml" ;
private const string JsonNamespaceUri = "http://james.newtonking.com/projects/json" ;
/// <summary>
/// Gets or sets the name of the root element to insert when deserializing to XML if the JSON structure has produced multiple root elements.
/// </summary>
/// <value>The name of the deserialized root element.</value>
2021-03-30 10:54:25 +08:00
public string DeserializeRootElementName { get ; set ; }
2021-03-29 14:54:12 +08:00
/// <summary>
/// Gets or sets a value to indicate whether to write the Json.NET array attribute.
/// This attribute helps preserve arrays when converting the written XML back to JSON.
/// </summary>
/// <value><c>true</c> if the array attribute is written to the XML; otherwise, <c>false</c>.</value>
public bool WriteArrayAttribute { get ; set ; }
/// <summary>
/// Gets or sets a value indicating whether to write the root JSON object.
/// </summary>
/// <value><c>true</c> if the JSON root object is omitted; otherwise, <c>false</c>.</value>
public bool OmitRootObject { get ; set ; }
/// <summary>
/// Gets or sets a value indicating whether to encode special characters when converting JSON to XML.
/// If <c>true</c>, special characters like ':', '@', '?', '#' and '$' in JSON property names aren't used to specify
/// XML namespaces, attributes or processing directives. Instead special characters are encoded and written
/// as part of the XML element name.
/// </summary>
/// <value><c>true</c> if special characters are encoded; otherwise, <c>false</c>.</value>
public bool EncodeSpecialCharacters { get ; set ; }
#region Writing
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="serializer">The calling serializer.</param>
/// <param name="value">The value.</param>
2021-03-30 10:54:25 +08:00
public override void WriteJson ( JsonWriter writer , object value , JsonSerializer serializer )
2021-03-29 14:54:12 +08:00
{
if ( value = = null )
{
writer . WriteNull ( ) ;
return ;
}
IXmlNode node = WrapXml ( value ) ;
XmlNamespaceManager manager = new XmlNamespaceManager ( new NameTable ( ) ) ;
PushParentNamespaces ( node , manager ) ;
if ( ! OmitRootObject )
{
writer . WriteStartObject ( ) ;
}
SerializeNode ( writer , node , manager , ! OmitRootObject ) ;
if ( ! OmitRootObject )
{
writer . WriteEndObject ( ) ;
}
}
private IXmlNode WrapXml ( object value )
{
#if HAVE_XLINQ
if ( value is XObject xObject )
{
return XContainerWrapper . WrapNode ( xObject ) ;
}
# endif
#if HAVE_XML_DOCUMENT
if ( value is XmlNode node )
{
return XmlNodeWrapper . WrapNode ( node ) ;
}
# endif
throw new ArgumentException ( "Value must be an XML object." , nameof ( value ) ) ;
}
private void PushParentNamespaces ( IXmlNode node , XmlNamespaceManager manager )
{
2021-03-30 10:54:25 +08:00
List < IXmlNode > parentElements = null ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
IXmlNode parent = node ;
2021-03-29 14:54:12 +08:00
while ( ( parent = parent . ParentNode ) ! = null )
{
if ( parent . NodeType = = XmlNodeType . Element )
{
if ( parentElements = = null )
{
parentElements = new List < IXmlNode > ( ) ;
}
parentElements . Add ( parent ) ;
}
}
if ( parentElements ! = null )
{
parentElements . Reverse ( ) ;
foreach ( IXmlNode parentElement in parentElements )
{
manager . PushScope ( ) ;
foreach ( IXmlNode attribute in parentElement . Attributes )
{
if ( attribute . NamespaceUri = = "http://www.w3.org/2000/xmlns/" & & attribute . LocalName ! = "xmlns" )
{
manager . AddNamespace ( attribute . LocalName , attribute . Value ) ;
}
}
}
}
}
private string ResolveFullName ( IXmlNode node , XmlNamespaceManager manager )
{
2021-03-30 10:54:25 +08:00
string prefix = ( node . NamespaceUri = = null | | ( node . LocalName = = "xmlns" & & node . NamespaceUri = = "http://www.w3.org/2000/xmlns/" ) )
2021-03-29 14:54:12 +08:00
? null
: manager . LookupPrefix ( node . NamespaceUri ) ;
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( prefix ) )
2021-03-29 14:54:12 +08:00
{
return prefix + ":" + XmlConvert . DecodeName ( node . LocalName ) ;
}
else
{
return XmlConvert . DecodeName ( node . LocalName ) ;
}
}
private string GetPropertyName ( IXmlNode node , XmlNamespaceManager manager )
{
switch ( node . NodeType )
{
case XmlNodeType . Attribute :
if ( node . NamespaceUri = = JsonNamespaceUri )
{
return "$" + node . LocalName ;
}
else
{
return "@" + ResolveFullName ( node , manager ) ;
}
case XmlNodeType . CDATA :
return CDataName ;
case XmlNodeType . Comment :
return CommentName ;
case XmlNodeType . Element :
if ( node . NamespaceUri = = JsonNamespaceUri )
{
return "$" + node . LocalName ;
}
else
{
return ResolveFullName ( node , manager ) ;
}
case XmlNodeType . ProcessingInstruction :
return "?" + ResolveFullName ( node , manager ) ;
case XmlNodeType . DocumentType :
return "!" + ResolveFullName ( node , manager ) ;
case XmlNodeType . XmlDeclaration :
return DeclarationName ;
case XmlNodeType . SignificantWhitespace :
return SignificantWhitespaceName ;
case XmlNodeType . Text :
return TextName ;
case XmlNodeType . Whitespace :
return WhitespaceName ;
default :
throw new JsonSerializationException ( "Unexpected XmlNodeType when getting node name: " + node . NodeType ) ;
}
}
private bool IsArray ( IXmlNode node )
{
foreach ( IXmlNode attribute in node . Attributes )
{
if ( attribute . LocalName = = "Array" & & attribute . NamespaceUri = = JsonNamespaceUri )
{
return XmlConvert . ToBoolean ( attribute . Value ) ;
}
}
return false ;
}
private void SerializeGroupedNodes ( JsonWriter writer , IXmlNode node , XmlNamespaceManager manager , bool writePropertyName )
{
switch ( node . ChildNodes . Count )
{
case 0 :
{
// nothing to serialize
break ;
}
case 1 :
{
// avoid grouping when there is only one node
string nodeName = GetPropertyName ( node . ChildNodes [ 0 ] , manager ) ;
WriteGroupedNodes ( writer , manager , writePropertyName , node . ChildNodes , nodeName ) ;
break ;
}
default :
{
// check whether nodes have the same name
// if they don't then group into dictionary together by name
// value of dictionary will be a single IXmlNode when there is one for a name,
// or a List<IXmlNode> when there are multiple
2021-03-30 10:54:25 +08:00
Dictionary < string , object > nodesGroupedByName = null ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
string nodeName = null ;
2021-03-29 14:54:12 +08:00
for ( int i = 0 ; i < node . ChildNodes . Count ; i + + )
{
IXmlNode childNode = node . ChildNodes [ i ] ;
string currentNodeName = GetPropertyName ( childNode , manager ) ;
if ( nodesGroupedByName = = null )
{
if ( nodeName = = null )
{
nodeName = currentNodeName ;
}
else if ( currentNodeName = = nodeName )
{
// current node name matches others
}
else
{
nodesGroupedByName = new Dictionary < string , object > ( ) ;
if ( i > 1 )
{
List < IXmlNode > nodes = new List < IXmlNode > ( i ) ;
for ( int j = 0 ; j < i ; j + + )
{
nodes . Add ( node . ChildNodes [ j ] ) ;
}
nodesGroupedByName . Add ( nodeName , nodes ) ;
}
else
{
nodesGroupedByName . Add ( nodeName , node . ChildNodes [ 0 ] ) ;
}
nodesGroupedByName . Add ( currentNodeName , childNode ) ;
}
}
else
{
if ( ! nodesGroupedByName . TryGetValue ( currentNodeName , out object value ) )
{
nodesGroupedByName . Add ( currentNodeName , childNode ) ;
}
else
{
if ( ! ( value is List < IXmlNode > nodes ) )
{
2021-03-30 10:54:25 +08:00
nodes = new List < IXmlNode > { ( IXmlNode ) value } ;
2021-03-29 14:54:12 +08:00
nodesGroupedByName [ currentNodeName ] = nodes ;
}
nodes . Add ( childNode ) ;
}
}
}
if ( nodesGroupedByName = = null )
{
2021-03-30 10:54:25 +08:00
WriteGroupedNodes ( writer , manager , writePropertyName , node . ChildNodes , nodeName ) ;
2021-03-29 14:54:12 +08:00
}
else
{
// loop through grouped nodes. write single name instances as normal,
// write multiple names together in an array
foreach ( KeyValuePair < string , object > nodeNameGroup in nodesGroupedByName )
{
if ( nodeNameGroup . Value is List < IXmlNode > nodes )
{
WriteGroupedNodes ( writer , manager , writePropertyName , nodes , nodeNameGroup . Key ) ;
}
else
{
WriteGroupedNodes ( writer , manager , writePropertyName , ( IXmlNode ) nodeNameGroup . Value , nodeNameGroup . Key ) ;
}
}
}
break ;
}
}
}
private void WriteGroupedNodes ( JsonWriter writer , XmlNamespaceManager manager , bool writePropertyName , List < IXmlNode > groupedNodes , string elementNames )
{
bool writeArray = groupedNodes . Count ! = 1 | | IsArray ( groupedNodes [ 0 ] ) ;
if ( ! writeArray )
{
SerializeNode ( writer , groupedNodes [ 0 ] , manager , writePropertyName ) ;
}
else
{
if ( writePropertyName )
{
writer . WritePropertyName ( elementNames ) ;
}
writer . WriteStartArray ( ) ;
for ( int i = 0 ; i < groupedNodes . Count ; i + + )
{
SerializeNode ( writer , groupedNodes [ i ] , manager , false ) ;
}
writer . WriteEndArray ( ) ;
}
}
private void WriteGroupedNodes ( JsonWriter writer , XmlNamespaceManager manager , bool writePropertyName , IXmlNode node , string elementNames )
{
bool writeArray = IsArray ( node ) ;
if ( ! writeArray )
{
SerializeNode ( writer , node , manager , writePropertyName ) ;
}
else
{
if ( writePropertyName )
{
writer . WritePropertyName ( elementNames ) ;
}
writer . WriteStartArray ( ) ;
SerializeNode ( writer , node , manager , false ) ;
writer . WriteEndArray ( ) ;
}
}
private void SerializeNode ( JsonWriter writer , IXmlNode node , XmlNamespaceManager manager , bool writePropertyName )
{
switch ( node . NodeType )
{
case XmlNodeType . Document :
case XmlNodeType . DocumentFragment :
SerializeGroupedNodes ( writer , node , manager , writePropertyName ) ;
break ;
case XmlNodeType . Element :
if ( IsArray ( node ) & & AllSameName ( node ) & & node . ChildNodes . Count > 0 )
{
SerializeGroupedNodes ( writer , node , manager , false ) ;
}
else
{
manager . PushScope ( ) ;
foreach ( IXmlNode attribute in node . Attributes )
{
if ( attribute . NamespaceUri = = "http://www.w3.org/2000/xmlns/" )
{
string namespacePrefix = ( attribute . LocalName ! = "xmlns" )
? XmlConvert . DecodeName ( attribute . LocalName )
: string . Empty ;
2021-03-30 10:54:25 +08:00
string namespaceUri = attribute . Value ;
2021-03-29 14:54:12 +08:00
manager . AddNamespace ( namespacePrefix , namespaceUri ) ;
}
}
if ( writePropertyName )
{
writer . WritePropertyName ( GetPropertyName ( node , manager ) ) ;
}
if ( ! ValueAttributes ( node . Attributes ) & & node . ChildNodes . Count = = 1
& & node . ChildNodes [ 0 ] . NodeType = = XmlNodeType . Text )
{
// write elements with a single text child as a name value pair
writer . WriteValue ( node . ChildNodes [ 0 ] . Value ) ;
}
else if ( node . ChildNodes . Count = = 0 & & node . Attributes . Count = = 0 )
{
IXmlElement element = ( IXmlElement ) node ;
// empty element
if ( element . IsEmpty )
{
writer . WriteNull ( ) ;
}
else
{
writer . WriteValue ( string . Empty ) ;
}
}
else
{
writer . WriteStartObject ( ) ;
for ( int i = 0 ; i < node . Attributes . Count ; i + + )
{
SerializeNode ( writer , node . Attributes [ i ] , manager , true ) ;
}
SerializeGroupedNodes ( writer , node , manager , true ) ;
writer . WriteEndObject ( ) ;
}
manager . PopScope ( ) ;
}
break ;
case XmlNodeType . Comment :
if ( writePropertyName )
{
writer . WriteComment ( node . Value ) ;
}
break ;
case XmlNodeType . Attribute :
case XmlNodeType . Text :
case XmlNodeType . CDATA :
case XmlNodeType . ProcessingInstruction :
case XmlNodeType . Whitespace :
case XmlNodeType . SignificantWhitespace :
if ( node . NamespaceUri = = "http://www.w3.org/2000/xmlns/" & & node . Value = = JsonNamespaceUri )
{
return ;
}
if ( node . NamespaceUri = = JsonNamespaceUri )
{
if ( node . LocalName = = "Array" )
{
return ;
}
}
if ( writePropertyName )
{
writer . WritePropertyName ( GetPropertyName ( node , manager ) ) ;
}
writer . WriteValue ( node . Value ) ;
break ;
case XmlNodeType . XmlDeclaration :
IXmlDeclaration declaration = ( IXmlDeclaration ) node ;
writer . WritePropertyName ( GetPropertyName ( node , manager ) ) ;
writer . WriteStartObject ( ) ;
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( declaration . Version ) )
2021-03-29 14:54:12 +08:00
{
writer . WritePropertyName ( "@version" ) ;
writer . WriteValue ( declaration . Version ) ;
}
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( declaration . Encoding ) )
2021-03-29 14:54:12 +08:00
{
writer . WritePropertyName ( "@encoding" ) ;
writer . WriteValue ( declaration . Encoding ) ;
}
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( declaration . Standalone ) )
2021-03-29 14:54:12 +08:00
{
writer . WritePropertyName ( "@standalone" ) ;
writer . WriteValue ( declaration . Standalone ) ;
}
writer . WriteEndObject ( ) ;
break ;
case XmlNodeType . DocumentType :
IXmlDocumentType documentType = ( IXmlDocumentType ) node ;
writer . WritePropertyName ( GetPropertyName ( node , manager ) ) ;
writer . WriteStartObject ( ) ;
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( documentType . Name ) )
2021-03-29 14:54:12 +08:00
{
writer . WritePropertyName ( "@name" ) ;
writer . WriteValue ( documentType . Name ) ;
}
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( documentType . Public ) )
2021-03-29 14:54:12 +08:00
{
writer . WritePropertyName ( "@public" ) ;
writer . WriteValue ( documentType . Public ) ;
}
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( documentType . System ) )
2021-03-29 14:54:12 +08:00
{
writer . WritePropertyName ( "@system" ) ;
writer . WriteValue ( documentType . System ) ;
}
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( documentType . InternalSubset ) )
2021-03-29 14:54:12 +08:00
{
writer . WritePropertyName ( "@internalSubset" ) ;
writer . WriteValue ( documentType . InternalSubset ) ;
}
writer . WriteEndObject ( ) ;
break ;
default :
throw new JsonSerializationException ( "Unexpected XmlNodeType when serializing nodes: " + node . NodeType ) ;
}
}
private static bool AllSameName ( IXmlNode node )
{
foreach ( IXmlNode childNode in node . ChildNodes )
{
if ( childNode . LocalName ! = node . LocalName )
{
return false ;
}
}
return true ;
}
# endregion
#region Reading
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>The object value.</returns>
2021-03-30 10:54:25 +08:00
public override object ReadJson ( JsonReader reader , Type objectType , object existingValue , JsonSerializer serializer )
2021-03-29 14:54:12 +08:00
{
switch ( reader . TokenType )
{
case JsonToken . Null :
return null ;
case JsonToken . StartObject :
break ;
default :
throw JsonSerializationException . Create ( reader , "XmlNodeConverter can only convert JSON that begins with an object." ) ;
}
XmlNamespaceManager manager = new XmlNamespaceManager ( new NameTable ( ) ) ;
2021-03-30 10:54:25 +08:00
IXmlDocument document = null ;
IXmlNode rootNode = null ;
2021-03-29 14:54:12 +08:00
#if HAVE_XLINQ
if ( typeof ( XObject ) . IsAssignableFrom ( objectType ) )
{
if ( objectType ! = typeof ( XContainer )
& & objectType ! = typeof ( XDocument )
& & objectType ! = typeof ( XElement )
& & objectType ! = typeof ( XNode )
& & objectType ! = typeof ( XObject ) )
{
throw JsonSerializationException . Create ( reader , "XmlNodeConverter only supports deserializing XDocument, XElement, XContainer, XNode or XObject." ) ;
}
XDocument d = new XDocument ( ) ;
document = new XDocumentWrapper ( d ) ;
rootNode = document ;
}
# endif
#if HAVE_XML_DOCUMENT
if ( typeof ( XmlNode ) . IsAssignableFrom ( objectType ) )
{
if ( objectType ! = typeof ( XmlDocument )
& & objectType ! = typeof ( XmlElement )
& & objectType ! = typeof ( XmlNode ) )
{
throw JsonSerializationException . Create ( reader , "XmlNodeConverter only supports deserializing XmlDocument, XmlElement or XmlNode." ) ;
}
XmlDocument d = new XmlDocument ( ) ;
#if HAVE_XML_DOCUMENT_TYPE
// prevent http request when resolving any DTD references
d . XmlResolver = null ;
# endif
document = new XmlDocumentWrapper ( d ) ;
rootNode = document ;
}
# endif
if ( document = = null | | rootNode = = null )
{
throw JsonSerializationException . Create ( reader , "Unexpected type when converting XML: " + objectType ) ;
}
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( DeserializeRootElementName ) )
2021-03-29 14:54:12 +08:00
{
ReadElement ( reader , document , rootNode , DeserializeRootElementName , manager ) ;
}
else
{
reader . ReadAndAssert ( ) ;
DeserializeNode ( reader , document , manager , rootNode ) ;
}
#if HAVE_XLINQ
if ( objectType = = typeof ( XElement ) )
{
2021-03-30 10:54:25 +08:00
XElement element = ( XElement ) document . DocumentElement . WrappedNode ;
2021-03-29 14:54:12 +08:00
element . Remove ( ) ;
return element ;
}
# endif
#if HAVE_XML_DOCUMENT
if ( objectType = = typeof ( XmlElement ) )
{
2021-03-30 10:54:25 +08:00
return document . DocumentElement . WrappedNode ;
2021-03-29 14:54:12 +08:00
}
# endif
return document . WrappedNode ;
}
private void DeserializeValue ( JsonReader reader , IXmlDocument document , XmlNamespaceManager manager , string propertyName , IXmlNode currentNode )
{
if ( ! EncodeSpecialCharacters )
{
switch ( propertyName )
{
case TextName :
currentNode . AppendChild ( document . CreateTextNode ( ConvertTokenToXmlValue ( reader ) ) ) ;
return ;
case CDataName :
currentNode . AppendChild ( document . CreateCDataSection ( ConvertTokenToXmlValue ( reader ) ) ) ;
return ;
case WhitespaceName :
currentNode . AppendChild ( document . CreateWhitespace ( ConvertTokenToXmlValue ( reader ) ) ) ;
return ;
case SignificantWhitespaceName :
currentNode . AppendChild ( document . CreateSignificantWhitespace ( ConvertTokenToXmlValue ( reader ) ) ) ;
return ;
default :
// processing instructions and the xml declaration start with ?
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( propertyName ) & & propertyName [ 0 ] = = '?' )
2021-03-29 14:54:12 +08:00
{
CreateInstruction ( reader , document , currentNode , propertyName ) ;
return ;
}
#if HAVE_XML_DOCUMENT_TYPE
else if ( string . Equals ( propertyName , "!DOCTYPE" , StringComparison . OrdinalIgnoreCase ) )
{
CreateDocumentType ( reader , document , currentNode ) ;
return ;
}
# endif
break ;
}
}
if ( reader . TokenType = = JsonToken . StartArray )
{
// handle nested arrays
ReadArrayElements ( reader , document , propertyName , currentNode , manager ) ;
return ;
}
// have to wait until attributes have been parsed before creating element
// attributes may contain namespace info used by the element
ReadElement ( reader , document , currentNode , propertyName , manager ) ;
}
private void ReadElement ( JsonReader reader , IXmlDocument document , IXmlNode currentNode , string propertyName , XmlNamespaceManager manager )
{
2021-03-30 10:54:25 +08:00
if ( string . IsNullOrEmpty ( propertyName ) )
2021-03-29 14:54:12 +08:00
{
throw JsonSerializationException . Create ( reader , "XmlNodeConverter cannot convert JSON with an empty property name to XML." ) ;
}
2021-03-30 10:54:25 +08:00
Dictionary < string , string > attributeNameValues = null ;
string elementPrefix = null ;
2021-03-29 14:54:12 +08:00
if ( ! EncodeSpecialCharacters )
{
attributeNameValues = ShouldReadInto ( reader )
? ReadAttributeElements ( reader , manager )
: null ;
elementPrefix = MiscellaneousUtils . GetPrefix ( propertyName ) ;
if ( propertyName . StartsWith ( '@' ) )
{
string attributeName = propertyName . Substring ( 1 ) ;
2021-03-30 10:54:25 +08:00
string attributePrefix = MiscellaneousUtils . GetPrefix ( attributeName ) ;
2021-03-29 14:54:12 +08:00
AddAttribute ( reader , document , currentNode , propertyName , attributeName , manager , attributePrefix ) ;
return ;
}
if ( propertyName . StartsWith ( '$' ) )
{
switch ( propertyName )
{
case JsonTypeReflector . ArrayValuesPropertyName :
propertyName = propertyName . Substring ( 1 ) ;
elementPrefix = manager . LookupPrefix ( JsonNamespaceUri ) ;
CreateElement ( reader , document , currentNode , propertyName , manager , elementPrefix , attributeNameValues ) ;
return ;
case JsonTypeReflector . IdPropertyName :
case JsonTypeReflector . RefPropertyName :
case JsonTypeReflector . TypePropertyName :
case JsonTypeReflector . ValuePropertyName :
string attributeName = propertyName . Substring ( 1 ) ;
string attributePrefix = manager . LookupPrefix ( JsonNamespaceUri ) ;
AddAttribute ( reader , document , currentNode , propertyName , attributeName , manager , attributePrefix ) ;
return ;
}
}
}
else
{
if ( ShouldReadInto ( reader ) )
{
reader . ReadAndAssert ( ) ;
}
}
CreateElement ( reader , document , currentNode , propertyName , manager , elementPrefix , attributeNameValues ) ;
}
2021-03-30 10:54:25 +08:00
private void CreateElement ( JsonReader reader , IXmlDocument document , IXmlNode currentNode , string elementName , XmlNamespaceManager manager , string elementPrefix , Dictionary < string , string > attributeNameValues )
2021-03-29 14:54:12 +08:00
{
IXmlElement element = CreateElement ( elementName , document , elementPrefix , manager ) ;
currentNode . AppendChild ( element ) ;
if ( attributeNameValues ! = null )
{
// add attributes to newly created element
2021-03-30 10:54:25 +08:00
foreach ( KeyValuePair < string , string > nameValue in attributeNameValues )
2021-03-29 14:54:12 +08:00
{
string encodedName = XmlConvert . EncodeName ( nameValue . Key ) ;
2021-03-30 10:54:25 +08:00
string attributePrefix = MiscellaneousUtils . GetPrefix ( nameValue . Key ) ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
IXmlNode attribute = ( ! string . IsNullOrEmpty ( attributePrefix ) ) ? document . CreateAttribute ( encodedName , manager . LookupNamespace ( attributePrefix ) ? ? string . Empty , nameValue . Value ) : document . CreateAttribute ( encodedName , nameValue . Value ) ;
2021-03-29 14:54:12 +08:00
element . SetAttributeNode ( attribute ) ;
}
}
switch ( reader . TokenType )
{
case JsonToken . String :
case JsonToken . Integer :
case JsonToken . Float :
case JsonToken . Boolean :
case JsonToken . Date :
case JsonToken . Bytes :
2021-03-30 10:54:25 +08:00
string text = ConvertTokenToXmlValue ( reader ) ;
2021-03-29 14:54:12 +08:00
if ( text ! = null )
{
element . AppendChild ( document . CreateTextNode ( text ) ) ;
}
break ;
case JsonToken . Null :
// empty element. do nothing
break ;
case JsonToken . EndObject :
// finished element will have no children to deserialize
manager . RemoveNamespace ( string . Empty , manager . DefaultNamespace ) ;
break ;
default :
manager . PushScope ( ) ;
DeserializeNode ( reader , document , manager , element ) ;
manager . PopScope ( ) ;
manager . RemoveNamespace ( string . Empty , manager . DefaultNamespace ) ;
break ;
}
}
2021-03-30 10:54:25 +08:00
private static void AddAttribute ( JsonReader reader , IXmlDocument document , IXmlNode currentNode , string propertyName , string attributeName , XmlNamespaceManager manager , string attributePrefix )
2021-03-29 14:54:12 +08:00
{
if ( currentNode . NodeType = = XmlNodeType . Document )
{
throw JsonSerializationException . Create ( reader , "JSON root object has property '{0}' that will be converted to an attribute. A root object cannot have any attribute properties. Consider specifying a DeserializeRootElementName." . FormatWith ( CultureInfo . InvariantCulture , propertyName ) ) ;
}
string encodedName = XmlConvert . EncodeName ( attributeName ) ;
2021-03-30 10:54:25 +08:00
string attributeValue = ConvertTokenToXmlValue ( reader ) ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
IXmlNode attribute = ( ! string . IsNullOrEmpty ( attributePrefix ) )
2021-03-29 14:54:12 +08:00
? document . CreateAttribute ( encodedName , manager . LookupNamespace ( attributePrefix ) , attributeValue )
: document . CreateAttribute ( encodedName , attributeValue ) ;
( ( IXmlElement ) currentNode ) . SetAttributeNode ( attribute ) ;
}
2021-03-30 10:54:25 +08:00
private static string ConvertTokenToXmlValue ( JsonReader reader )
2021-03-29 14:54:12 +08:00
{
switch ( reader . TokenType )
{
case JsonToken . String :
return reader . Value ? . ToString ( ) ;
case JsonToken . Integer :
#if HAVE_BIG_INTEGER
if ( reader . Value is BigInteger i )
{
return i . ToString ( CultureInfo . InvariantCulture ) ;
}
# endif
return XmlConvert . ToString ( Convert . ToInt64 ( reader . Value , CultureInfo . InvariantCulture ) ) ;
case JsonToken . Float :
{
if ( reader . Value is decimal d )
{
return XmlConvert . ToString ( d ) ;
}
if ( reader . Value is float f )
{
return XmlConvert . ToString ( f ) ;
}
return XmlConvert . ToString ( Convert . ToDouble ( reader . Value , CultureInfo . InvariantCulture ) ) ;
}
case JsonToken . Boolean :
return XmlConvert . ToString ( Convert . ToBoolean ( reader . Value , CultureInfo . InvariantCulture ) ) ;
case JsonToken . Date :
{
#if HAVE_DATE_TIME_OFFSET
if ( reader . Value is DateTimeOffset offset )
{
return XmlConvert . ToString ( offset ) ;
}
# endif
DateTime d = Convert . ToDateTime ( reader . Value , CultureInfo . InvariantCulture ) ;
#if !PORTABLE || NETSTANDARD1_3
return XmlConvert . ToString ( d , DateTimeUtils . ToSerializationMode ( d . Kind ) ) ;
# else
return d . ToString ( DateTimeUtils . ToDateTimeFormat ( d . Kind ) , CultureInfo . InvariantCulture ) ;
# endif
}
case JsonToken . Bytes :
2021-03-30 10:54:25 +08:00
return Convert . ToBase64String ( ( byte [ ] ) reader . Value ) ;
2021-03-29 14:54:12 +08:00
case JsonToken . Null :
return null ;
default :
throw JsonSerializationException . Create ( reader , "Cannot get an XML string value from token type '{0}'." . FormatWith ( CultureInfo . InvariantCulture , reader . TokenType ) ) ;
}
}
private void ReadArrayElements ( JsonReader reader , IXmlDocument document , string propertyName , IXmlNode currentNode , XmlNamespaceManager manager )
{
2021-03-30 10:54:25 +08:00
string elementPrefix = MiscellaneousUtils . GetPrefix ( propertyName ) ;
2021-03-29 14:54:12 +08:00
IXmlElement nestedArrayElement = CreateElement ( propertyName , document , elementPrefix , manager ) ;
currentNode . AppendChild ( nestedArrayElement ) ;
int count = 0 ;
while ( reader . Read ( ) & & reader . TokenType ! = JsonToken . EndArray )
{
DeserializeValue ( reader , document , manager , propertyName , nestedArrayElement ) ;
count + + ;
}
if ( WriteArrayAttribute )
{
AddJsonArrayAttribute ( nestedArrayElement , document ) ;
}
if ( count = = 1 & & WriteArrayAttribute )
{
foreach ( IXmlNode childNode in nestedArrayElement . ChildNodes )
{
if ( childNode is IXmlElement element & & element . LocalName = = propertyName )
{
AddJsonArrayAttribute ( element , document ) ;
break ;
}
}
}
}
private void AddJsonArrayAttribute ( IXmlElement element , IXmlDocument document )
{
element . SetAttributeNode ( document . CreateAttribute ( "json:Array" , JsonNamespaceUri , "true" ) ) ;
#if HAVE_XLINQ
// linq to xml doesn't automatically include prefixes via the namespace manager
if ( element is XElementWrapper )
{
if ( element . GetPrefixOfNamespace ( JsonNamespaceUri ) = = null )
{
element . SetAttributeNode ( document . CreateAttribute ( "xmlns:json" , "http://www.w3.org/2000/xmlns/" , JsonNamespaceUri ) ) ;
}
}
# endif
}
private bool ShouldReadInto ( JsonReader reader )
{
// a string token means the element only has a single text child
switch ( reader . TokenType )
{
case JsonToken . String :
case JsonToken . Null :
case JsonToken . Boolean :
case JsonToken . Integer :
case JsonToken . Float :
case JsonToken . Date :
case JsonToken . Bytes :
case JsonToken . StartConstructor :
return false ;
}
return true ;
}
2021-03-30 10:54:25 +08:00
private Dictionary < string , string > ReadAttributeElements ( JsonReader reader , XmlNamespaceManager manager )
2021-03-29 14:54:12 +08:00
{
2021-03-30 10:54:25 +08:00
Dictionary < string , string > attributeNameValues = null ;
2021-03-29 14:54:12 +08:00
bool finished = false ;
// read properties until first non-attribute is encountered
while ( ! finished & & reader . Read ( ) )
{
switch ( reader . TokenType )
{
case JsonToken . PropertyName :
2021-03-30 10:54:25 +08:00
string attributeName = reader . Value . ToString ( ) ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
if ( ! string . IsNullOrEmpty ( attributeName ) )
2021-03-29 14:54:12 +08:00
{
char firstChar = attributeName [ 0 ] ;
2021-03-30 10:54:25 +08:00
string attributeValue ;
2021-03-29 14:54:12 +08:00
switch ( firstChar )
{
case '@' :
if ( attributeNameValues = = null )
{
2021-03-30 10:54:25 +08:00
attributeNameValues = new Dictionary < string , string > ( ) ;
2021-03-29 14:54:12 +08:00
}
attributeName = attributeName . Substring ( 1 ) ;
reader . ReadAndAssert ( ) ;
attributeValue = ConvertTokenToXmlValue ( reader ) ;
attributeNameValues . Add ( attributeName , attributeValue ) ;
2021-03-30 10:54:25 +08:00
if ( IsNamespaceAttribute ( attributeName , out string namespacePrefix ) )
2021-03-29 14:54:12 +08:00
{
manager . AddNamespace ( namespacePrefix , attributeValue ) ;
}
break ;
case '$' :
switch ( attributeName )
{
case JsonTypeReflector . ArrayValuesPropertyName :
case JsonTypeReflector . IdPropertyName :
case JsonTypeReflector . RefPropertyName :
case JsonTypeReflector . TypePropertyName :
case JsonTypeReflector . ValuePropertyName :
// check that JsonNamespaceUri is in scope
// if it isn't then add it to document and namespace manager
string jsonPrefix = manager . LookupPrefix ( JsonNamespaceUri ) ;
if ( jsonPrefix = = null )
{
if ( attributeNameValues = = null )
{
2021-03-30 10:54:25 +08:00
attributeNameValues = new Dictionary < string , string > ( ) ;
2021-03-29 14:54:12 +08:00
}
// ensure that the prefix used is free
int? i = null ;
while ( manager . LookupNamespace ( "json" + i ) ! = null )
{
i = i . GetValueOrDefault ( ) + 1 ;
}
jsonPrefix = "json" + i ;
attributeNameValues . Add ( "xmlns:" + jsonPrefix , JsonNamespaceUri ) ;
manager . AddNamespace ( jsonPrefix , JsonNamespaceUri ) ;
}
// special case $values, it will have a non-primitive value
if ( attributeName = = JsonTypeReflector . ArrayValuesPropertyName )
{
finished = true ;
break ;
}
attributeName = attributeName . Substring ( 1 ) ;
reader . ReadAndAssert ( ) ;
if ( ! JsonTokenUtils . IsPrimitiveToken ( reader . TokenType ) )
{
throw JsonSerializationException . Create ( reader , "Unexpected JsonToken: " + reader . TokenType ) ;
}
if ( attributeNameValues = = null )
{
2021-03-30 10:54:25 +08:00
attributeNameValues = new Dictionary < string , string > ( ) ;
2021-03-29 14:54:12 +08:00
}
attributeValue = reader . Value ? . ToString ( ) ;
attributeNameValues . Add ( jsonPrefix + ":" + attributeName , attributeValue ) ;
break ;
default :
finished = true ;
break ;
}
break ;
default :
finished = true ;
break ;
}
}
else
{
finished = true ;
}
break ;
case JsonToken . EndObject :
case JsonToken . Comment :
finished = true ;
break ;
default :
throw JsonSerializationException . Create ( reader , "Unexpected JsonToken: " + reader . TokenType ) ;
}
}
return attributeNameValues ;
}
private void CreateInstruction ( JsonReader reader , IXmlDocument document , IXmlNode currentNode , string propertyName )
{
if ( propertyName = = DeclarationName )
{
2021-03-30 10:54:25 +08:00
string version = null ;
string encoding = null ;
string standalone = null ;
2021-03-29 14:54:12 +08:00
while ( reader . Read ( ) & & reader . TokenType ! = JsonToken . EndObject )
{
2021-03-30 10:54:25 +08:00
switch ( reader . Value . ToString ( ) )
2021-03-29 14:54:12 +08:00
{
case "@version" :
reader . ReadAndAssert ( ) ;
version = ConvertTokenToXmlValue ( reader ) ;
break ;
case "@encoding" :
reader . ReadAndAssert ( ) ;
encoding = ConvertTokenToXmlValue ( reader ) ;
break ;
case "@standalone" :
reader . ReadAndAssert ( ) ;
standalone = ConvertTokenToXmlValue ( reader ) ;
break ;
default :
throw JsonSerializationException . Create ( reader , "Unexpected property name encountered while deserializing XmlDeclaration: " + reader . Value ) ;
}
}
IXmlNode declaration = document . CreateXmlDeclaration ( version , encoding , standalone ) ;
currentNode . AppendChild ( declaration ) ;
}
else
{
IXmlNode instruction = document . CreateProcessingInstruction ( propertyName . Substring ( 1 ) , ConvertTokenToXmlValue ( reader ) ) ;
currentNode . AppendChild ( instruction ) ;
}
}
#if HAVE_XML_DOCUMENT_TYPE
private void CreateDocumentType ( JsonReader reader , IXmlDocument document , IXmlNode currentNode )
{
2021-03-30 10:54:25 +08:00
string name = null ;
string publicId = null ;
string systemId = null ;
string internalSubset = null ;
2021-03-29 14:54:12 +08:00
while ( reader . Read ( ) & & reader . TokenType ! = JsonToken . EndObject )
{
2021-03-30 10:54:25 +08:00
switch ( reader . Value . ToString ( ) )
2021-03-29 14:54:12 +08:00
{
case "@name" :
reader . ReadAndAssert ( ) ;
name = ConvertTokenToXmlValue ( reader ) ;
break ;
case "@public" :
reader . ReadAndAssert ( ) ;
publicId = ConvertTokenToXmlValue ( reader ) ;
break ;
case "@system" :
reader . ReadAndAssert ( ) ;
systemId = ConvertTokenToXmlValue ( reader ) ;
break ;
case "@internalSubset" :
reader . ReadAndAssert ( ) ;
internalSubset = ConvertTokenToXmlValue ( reader ) ;
break ;
default :
throw JsonSerializationException . Create ( reader , "Unexpected property name encountered while deserializing XmlDeclaration: " + reader . Value ) ;
}
}
IXmlNode documentType = document . CreateXmlDocumentType ( name , publicId , systemId , internalSubset ) ;
currentNode . AppendChild ( documentType ) ;
}
# endif
2021-03-30 10:54:25 +08:00
private IXmlElement CreateElement ( string elementName , IXmlDocument document , string elementPrefix , XmlNamespaceManager manager )
2021-03-29 14:54:12 +08:00
{
string encodeName = EncodeSpecialCharacters ? XmlConvert . EncodeLocalName ( elementName ) : XmlConvert . EncodeName ( elementName ) ;
2021-03-30 10:54:25 +08:00
string ns = string . IsNullOrEmpty ( elementPrefix ) ? manager . DefaultNamespace : manager . LookupNamespace ( elementPrefix ) ;
2021-03-29 14:54:12 +08:00
2021-03-30 10:54:25 +08:00
IXmlElement element = ( ! string . IsNullOrEmpty ( ns ) ) ? document . CreateElement ( encodeName , ns ) : document . CreateElement ( encodeName ) ;
2021-03-29 14:54:12 +08:00
return element ;
}
private void DeserializeNode ( JsonReader reader , IXmlDocument document , XmlNamespaceManager manager , IXmlNode currentNode )
{
do
{
switch ( reader . TokenType )
{
case JsonToken . PropertyName :
if ( currentNode . NodeType = = XmlNodeType . Document & & document . DocumentElement ! = null )
{
throw JsonSerializationException . Create ( reader , "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifying a DeserializeRootElementName." ) ;
}
2021-03-30 10:54:25 +08:00
string propertyName = reader . Value . ToString ( ) ;
2021-03-29 14:54:12 +08:00
reader . ReadAndAssert ( ) ;
if ( reader . TokenType = = JsonToken . StartArray )
{
int count = 0 ;
while ( reader . Read ( ) & & reader . TokenType ! = JsonToken . EndArray )
{
DeserializeValue ( reader , document , manager , propertyName , currentNode ) ;
count + + ;
}
if ( count = = 1 & & WriteArrayAttribute )
{
2021-03-30 10:54:25 +08:00
MiscellaneousUtils . GetQualifiedNameParts ( propertyName , out string elementPrefix , out string localName ) ;
string ns = string . IsNullOrEmpty ( elementPrefix ) ? manager . DefaultNamespace : manager . LookupNamespace ( elementPrefix ) ;
2021-03-29 14:54:12 +08:00
foreach ( IXmlNode childNode in currentNode . ChildNodes )
{
if ( childNode is IXmlElement element & & element . LocalName = = localName & & element . NamespaceUri = = ns )
{
AddJsonArrayAttribute ( element , document ) ;
break ;
}
}
}
}
else
{
DeserializeValue ( reader , document , manager , propertyName , currentNode ) ;
}
continue ;
case JsonToken . StartConstructor :
2021-03-30 10:54:25 +08:00
string constructorName = reader . Value . ToString ( ) ;
2021-03-29 14:54:12 +08:00
while ( reader . Read ( ) & & reader . TokenType ! = JsonToken . EndConstructor )
{
DeserializeValue ( reader , document , manager , constructorName , currentNode ) ;
}
break ;
case JsonToken . Comment :
2021-03-30 10:54:25 +08:00
currentNode . AppendChild ( document . CreateComment ( ( string ) reader . Value ) ) ;
2021-03-29 14:54:12 +08:00
break ;
case JsonToken . EndObject :
case JsonToken . EndArray :
return ;
default :
throw JsonSerializationException . Create ( reader , "Unexpected JsonToken when deserializing node: " + reader . TokenType ) ;
}
} while ( reader . Read ( ) ) ;
// don't read if current token is a property. token was already read when parsing element attributes
}
/// <summary>
/// Checks if the <paramref name="attributeName"/> is a namespace attribute.
/// </summary>
/// <param name="attributeName">Attribute name to test.</param>
/// <param name="prefix">The attribute name prefix if it has one, otherwise an empty string.</param>
/// <returns><c>true</c> if attribute name is for a namespace attribute, otherwise <c>false</c>.</returns>
2021-03-30 10:54:25 +08:00
private bool IsNamespaceAttribute ( string attributeName , out string prefix )
2021-03-29 14:54:12 +08:00
{
if ( attributeName . StartsWith ( "xmlns" , StringComparison . Ordinal ) )
{
if ( attributeName . Length = = 5 )
{
prefix = string . Empty ;
return true ;
}
else if ( attributeName [ 5 ] = = ':' )
{
prefix = attributeName . Substring ( 6 , attributeName . Length - 6 ) ;
return true ;
}
}
prefix = null ;
return false ;
}
private bool ValueAttributes ( List < IXmlNode > c )
{
foreach ( IXmlNode xmlNode in c )
{
if ( xmlNode . NamespaceUri = = JsonNamespaceUri )
{
continue ;
}
if ( xmlNode . NamespaceUri = = "http://www.w3.org/2000/xmlns/" & & xmlNode . Value = = JsonNamespaceUri )
{
continue ;
}
return true ;
}
return false ;
}
# endregion
/// <summary>
/// Determines whether this instance can convert the specified value type.
/// </summary>
/// <param name="valueType">Type of the value.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified value type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert ( Type valueType )
{
#if HAVE_XLINQ
if ( valueType . AssignableToTypeName ( "System.Xml.Linq.XObject" , false ) )
{
return IsXObject ( valueType ) ;
}
# endif
#if HAVE_XML_DOCUMENT
if ( valueType . AssignableToTypeName ( "System.Xml.XmlNode" , false ) )
{
return IsXmlNode ( valueType ) ;
}
# endif
return false ;
}
#if HAVE_XLINQ
[MethodImpl(MethodImplOptions.NoInlining)]
private bool IsXObject ( Type valueType )
{
return typeof ( XObject ) . IsAssignableFrom ( valueType ) ;
}
# endif
#if HAVE_XML_DOCUMENT
[MethodImpl(MethodImplOptions.NoInlining)]
private bool IsXmlNode ( Type valueType )
{
return typeof ( XmlNode ) . IsAssignableFrom ( valueType ) ;
}
# endif
}
}
# endif