#region copyright //------------------------------------------------------------------------------ // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 // 此代码版权(除特别声明外的代码)归作者本人Diego所有 // 源代码使用协议遵循本仓库的开源协议及附加协议 // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway // 使用文档:https://diego2098.gitee.io/thingsgateway-docs/ // QQ群:605534569 //------------------------------------------------------------------------------ #endregion using System.Globalization; using System.IO; using System.Text; using System.Xml; #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 namespace Opc.Ua { public class OPCUAJsonEncoder : IJsonEncoder, IEncoder, IDisposable { private const int kStreamWriterBufferSize = 1024; private Stream m_stream; private MemoryStream m_memoryStream; private StreamWriter m_writer; private Stack m_namespaces; private bool m_commaRequired; private bool m_inVariantWithEncoding; private IServiceMessageContext m_context; private ushort[] m_namespaceMappings; private ushort[] m_serverMappings; private uint m_nestingLevel; private bool m_topLevelIsArray; private bool m_levelOneSkipped; private bool m_dontWriteClosing; private bool m_leaveOpen; private static readonly char[] m_specialChars = new char[7] { '"', '\\', '\n', '\r', '\t', '\b', '\f' }; private static readonly char[] m_substitution = new char[7] { '"', '\\', 'n', 'r', 't', 'b', 'f' }; public EncodingType EncodingType => EncodingType.Json; public IServiceMessageContext Context => m_context; public bool UseReversibleEncoding { get; private set; } public bool ForceNamespaceUri { get; set; } public bool ForceNamespaceUriForIndex1 { get; set; } public bool IncludeDefaultValues { get; set; } public bool IncludeDefaultNumberValues { get; set; } public OPCUAJsonEncoder(IServiceMessageContext context, bool useReversibleEncoding) : this(context, useReversibleEncoding, topLevelIsArray: false, null, leaveOpen: false, 1024) { } public OPCUAJsonEncoder(IServiceMessageContext context, bool useReversibleEncoding, bool topLevelIsArray = false, Stream stream = null, bool leaveOpen = false, int streamSize = 1024) { Initialize(); m_context = context; m_stream = stream; m_leaveOpen = leaveOpen; UseReversibleEncoding = useReversibleEncoding; m_topLevelIsArray = topLevelIsArray; if (m_stream == null) { m_memoryStream = new MemoryStream(); m_writer = new StreamWriter(m_memoryStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), streamSize, leaveOpen: false); m_leaveOpen = false; } else { m_writer = new StreamWriter(m_stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), streamSize, m_leaveOpen); } InitializeWriter(); } public OPCUAJsonEncoder(IServiceMessageContext context, bool useReversibleEncoding, StreamWriter writer, bool topLevelIsArray = false) { Initialize(); m_context = context; m_writer = writer; UseReversibleEncoding = useReversibleEncoding; m_topLevelIsArray = topLevelIsArray; if (m_writer == null) { m_stream = new MemoryStream(); m_writer = new StreamWriter(m_stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), 1024); } InitializeWriter(); } private void Initialize() { m_stream = null; m_writer = null; m_namespaces = new Stack(); m_commaRequired = false; m_leaveOpen = false; m_nestingLevel = 0u; m_levelOneSkipped = false; ForceNamespaceUri = false; IncludeDefaultValues = false; IncludeDefaultNumberValues = true; } private void InitializeWriter() { if (m_topLevelIsArray) { m_writer.Write("["); } else { m_writer.Write("{"); } } public static void EncodeSessionLessMessage(IEncodeable message, Stream stream, IServiceMessageContext context, bool leaveOpen) { if (message == null) { throw new ArgumentNullException("message"); } if (context == null) { throw new ArgumentNullException("context"); } OPCUAJsonEncoder jsonEncoder = new OPCUAJsonEncoder(context, useReversibleEncoding: true, topLevelIsArray: false, stream, leaveOpen); try { long position = stream.Position; SessionLessServiceMessage sessionLessServiceMessage = new SessionLessServiceMessage(); sessionLessServiceMessage.NamespaceUris = context.NamespaceUris; sessionLessServiceMessage.ServerUris = context.ServerUris; sessionLessServiceMessage.Message = message; sessionLessServiceMessage.Encode(jsonEncoder); if (context.MaxMessageSize > 0 && context.MaxMessageSize < (int)(stream.Position - position)) { throw ServiceResultException.Create(2148007936u, "MaxMessageSize {0} < {1}", context.MaxMessageSize, (int)(stream.Position - position)); } jsonEncoder.Close(); } finally { if (leaveOpen) { stream.Position = 0L; } jsonEncoder.Dispose(); } } public static ArraySegment EncodeMessage(IEncodeable message, byte[] buffer, IServiceMessageContext context) { if (message == null) { throw new ArgumentNullException("message"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (context == null) { throw new ArgumentNullException("context"); } using MemoryStream stream = new MemoryStream(buffer, writable: true); using OPCUAJsonEncoder jsonEncoder = new OPCUAJsonEncoder(context, useReversibleEncoding: true, topLevelIsArray: false, stream); jsonEncoder.EncodeMessage(message); int count = jsonEncoder.Close(); return new ArraySegment(buffer, 0, count); } public void EncodeMessage(IEncodeable message) { if (message == null) { throw new ArgumentNullException("message"); } NodeId value = ExpandedNodeId.ToNodeId(message.TypeId, m_context.NamespaceUris); WriteNodeId("TypeId", value); WriteEncodeable("Body", message, message.GetType()); } public void SetMappingTables(NamespaceTable namespaceUris, StringTable serverUris) { m_namespaceMappings = null; if (namespaceUris != null && m_context.NamespaceUris != null) { m_namespaceMappings = namespaceUris.CreateMapping(m_context.NamespaceUris, updateTable: false); } m_serverMappings = null; if (serverUris != null && m_context.ServerUris != null) { m_serverMappings = serverUris.CreateMapping(m_context.ServerUris, updateTable: false); } } public string CloseAndReturnText() { Close(); if (m_memoryStream == null) { MemoryStream memoryStream = m_stream as MemoryStream; if (memoryStream != null) { return Encoding.UTF8.GetString(memoryStream.ToArray()); } throw new NotSupportedException("Cannot get text from external stream. Use Close or MemoryStream instead."); } return Encoding.UTF8.GetString(m_memoryStream.ToArray()); } public int Close() { if (!m_dontWriteClosing) { if (m_topLevelIsArray) { m_writer.Write("]"); } else { m_writer.Write("}"); } } m_writer.Flush(); int result = (int)m_writer.BaseStream.Position; m_writer.Dispose(); m_writer = null; return result; } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (m_writer != null) { Close(); } if (!m_leaveOpen) { Utils.SilentDispose(m_memoryStream); Utils.SilentDispose(m_stream); m_memoryStream = null; m_stream = null; } } } public void PushStructure(string fieldName) { m_nestingLevel++; if (m_commaRequired) { m_writer.Write(","); } if (!string.IsNullOrEmpty(fieldName)) { m_writer.Write("\""); EscapeString(fieldName); m_writer.Write("\":"); } else if (!m_commaRequired && m_nestingLevel == 1 && !m_topLevelIsArray) { m_levelOneSkipped = true; return; } m_commaRequired = false; m_writer.Write("{"); } public void PushArray(string fieldName) { m_nestingLevel++; if (m_commaRequired) { m_writer.Write(","); } if (!string.IsNullOrEmpty(fieldName)) { m_writer.Write("\""); EscapeString(fieldName); m_writer.Write("\":"); } else if (!m_commaRequired && m_nestingLevel == 1 && !m_topLevelIsArray) { m_levelOneSkipped = true; return; } m_commaRequired = false; m_writer.Write("["); } public void PopStructure() { if (m_nestingLevel > 1 || m_topLevelIsArray || (m_nestingLevel == 1 && !m_levelOneSkipped)) { m_writer.Write("}"); m_commaRequired = true; } m_nestingLevel--; } public void PopArray() { if (m_nestingLevel > 1 || m_topLevelIsArray || (m_nestingLevel == 1 && !m_levelOneSkipped)) { m_writer.Write("]"); m_commaRequired = true; } m_nestingLevel--; } public void UsingReversibleEncoding(Action action, string fieldName, T value, bool useReversibleEncoding) { bool useReversibleEncoding2 = UseReversibleEncoding; try { UseReversibleEncoding = useReversibleEncoding; action(fieldName, value); } finally { UseReversibleEncoding = useReversibleEncoding2; } } public void PushNamespace(string namespaceUri) { m_namespaces.Push(namespaceUri); } public void PopNamespace() { m_namespaces.Pop(); } private void EscapeString(string value) { foreach (char c in value) { bool flag = false; for (int j = 0; j < m_specialChars.Length; j++) { if (m_specialChars[j] == c) { m_writer.Write('\\'); m_writer.Write(m_substitution[j]); flag = true; break; } } if (!flag) { if (c < ' ') { m_writer.Write("\\u"); m_writer.Write("{0:X4}", (int)c); } else { m_writer.Write(c); } } } } private void WriteSimpleField(string fieldName, string value, bool quotes) { if (!string.IsNullOrEmpty(fieldName)) { if (value == null) { return; } if (m_commaRequired) { m_writer.Write(","); } m_writer.Write("\""); EscapeString(fieldName); m_writer.Write("\":"); } else if (m_commaRequired) { m_writer.Write(","); } if (value != null) { if (quotes) { m_writer.Write("\""); EscapeString(value); m_writer.Write("\""); } else { m_writer.Write(value); } } else { m_writer.Write("null"); } m_commaRequired = true; } public void WriteBoolean(string fieldName, bool value) { if (fieldName != null && !IncludeDefaultNumberValues && !value) { WriteSimpleField(fieldName, null, quotes: false); } else if (value) { WriteSimpleField(fieldName, "true", quotes: false); } else { WriteSimpleField(fieldName, "false", quotes: false); } } public void WriteSByte(string fieldName, sbyte value) { if (fieldName != null && !IncludeDefaultNumberValues && value == 0) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(CultureInfo.InvariantCulture), quotes: false); } } public void WriteByte(string fieldName, byte value) { if (fieldName != null && !IncludeDefaultNumberValues && value == 0) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(CultureInfo.InvariantCulture), quotes: false); } } public void WriteInt16(string fieldName, short value) { if (fieldName != null && !IncludeDefaultNumberValues && value == 0) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(CultureInfo.InvariantCulture), quotes: false); } } public void WriteUInt16(string fieldName, ushort value) { if (fieldName != null && !IncludeDefaultNumberValues && value == 0) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(CultureInfo.InvariantCulture), quotes: false); } } public void WriteInt32(string fieldName, int value) { if (fieldName != null && !IncludeDefaultNumberValues && value == 0) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(CultureInfo.InvariantCulture), quotes: false); } } public void WriteUInt32(string fieldName, uint value) { if (fieldName != null && !IncludeDefaultNumberValues && value == 0) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(CultureInfo.InvariantCulture), quotes: false); } } public void WriteInt64(string fieldName, long value) { if (fieldName != null && !IncludeDefaultNumberValues && value == 0L) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(CultureInfo.InvariantCulture), quotes: true); } } public void WriteUInt64(string fieldName, ulong value) { if (fieldName != null && !IncludeDefaultNumberValues && value == 0L) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(CultureInfo.InvariantCulture), quotes: true); } } public void WriteFloat(string fieldName, float value) { if (fieldName != null && !IncludeDefaultNumberValues && value > -1.401298E-45f && value < float.Epsilon) { WriteSimpleField(fieldName, null, quotes: false); } else if (float.IsNaN(value)) { WriteSimpleField(fieldName, "NaN", quotes: true); } else if (float.IsPositiveInfinity(value)) { WriteSimpleField(fieldName, "Infinity", quotes: true); } else if (float.IsNegativeInfinity(value)) { WriteSimpleField(fieldName, "-Infinity", quotes: true); } else { WriteSimpleField(fieldName, value.ToString("R", CultureInfo.InvariantCulture), quotes: false); } } public void WriteDouble(string fieldName, double value) { if (fieldName != null && !IncludeDefaultNumberValues && value > -4.94065645841247E-324 && value < double.Epsilon) { WriteSimpleField(fieldName, null, quotes: false); } else if (double.IsNaN(value)) { WriteSimpleField(fieldName, "NaN", quotes: true); } else if (double.IsPositiveInfinity(value)) { WriteSimpleField(fieldName, "Infinity", quotes: true); } else if (double.IsNegativeInfinity(value)) { WriteSimpleField(fieldName, "-Infinity", quotes: true); } else { WriteSimpleField(fieldName, value.ToString("R", CultureInfo.InvariantCulture), quotes: false); } } public void WriteString(string fieldName, string value) { if (fieldName != null && !IncludeDefaultValues && value == null) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value, quotes: true); } } public void WriteDateTime(string fieldName, DateTime value) { if (fieldName != null && !IncludeDefaultValues && value == DateTime.MinValue) { WriteSimpleField(fieldName, null, quotes: false); } else if (value <= DateTime.MinValue) { WriteSimpleField(fieldName, "0001-01-01T00:00:00Z", quotes: true); } else if (value >= DateTime.MaxValue) { WriteSimpleField(fieldName, "9999-12-31T23:59:59Z", quotes: true); } else { WriteSimpleField(fieldName, value.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.FFFFFFFK", CultureInfo.InvariantCulture), quotes: true); } } public void WriteGuid(string fieldName, Uuid value) { if (fieldName != null && !IncludeDefaultValues && value == Uuid.Empty) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(), quotes: true); } } public void WriteGuid(string fieldName, Guid value) { if (fieldName != null && !IncludeDefaultValues && value == Guid.Empty) { WriteSimpleField(fieldName, null, quotes: false); } else { WriteSimpleField(fieldName, value.ToString(), quotes: true); } } public void WriteByteString(string fieldName, byte[] value) { if (value == null) { WriteSimpleField(fieldName, null, quotes: false); return; } if (m_context.MaxByteStringLength > 0 && m_context.MaxByteStringLength < value.Length) { throw new ServiceResultException(2148007936u); } WriteSimpleField(fieldName, Convert.ToBase64String(value), quotes: true); } public void WriteXmlElement(string fieldName, XmlElement value) { if (value == null) { WriteSimpleField(fieldName, null, quotes: false); return; } string outerXml = value.OuterXml; byte[] bytes = Encoding.UTF8.GetBytes(outerXml); WriteSimpleField(fieldName, Convert.ToBase64String(bytes), quotes: true); } private void WriteNamespaceIndex(string fieldName, ushort namespaceIndex) { if (namespaceIndex == 0) { return; } if ((!UseReversibleEncoding || ForceNamespaceUri) && namespaceIndex > ((!ForceNamespaceUriForIndex1) ? 1 : 0)) { string @string = m_context.NamespaceUris.GetString(namespaceIndex); if (!string.IsNullOrEmpty(@string)) { WriteSimpleField(fieldName, @string, quotes: true); return; } } if (m_namespaceMappings != null && m_namespaceMappings.Length > namespaceIndex) { namespaceIndex = m_namespaceMappings[namespaceIndex]; } if (namespaceIndex != 0) { WriteUInt16(fieldName, namespaceIndex); } } private void WriteNodeIdContents(NodeId value, string namespaceUri = null) { if (value.IdType > IdType.Numeric) { WriteInt32("IdType", (int)value.IdType); } switch (value.IdType) { case IdType.Numeric: WriteUInt32("Id", (uint)value.Identifier); break; case IdType.String: WriteString("Id", (string)value.Identifier); break; case IdType.Guid: WriteGuid("Id", (Guid)value.Identifier); break; case IdType.Opaque: WriteByteString("Id", (byte[])value.Identifier); break; } if (namespaceUri != null) { WriteString("Namespace", namespaceUri); } else { WriteNamespaceIndex("Namespace", value.NamespaceIndex); } } public void WriteNodeId(string fieldName, NodeId value) { if (value == null || (NodeId.IsNull(value) && value.IdType == IdType.Numeric)) { WriteSimpleField(fieldName, null, quotes: false); return; } PushStructure(fieldName); ushort namespaceIndex = value.NamespaceIndex; if (ForceNamespaceUri && namespaceIndex > ((!ForceNamespaceUriForIndex1) ? 1 : 0)) { string @string = Context.NamespaceUris.GetString(namespaceIndex); WriteNodeIdContents(value, @string); } else { WriteNodeIdContents(value); } PopStructure(); } public void WriteExpandedNodeId(string fieldName, ExpandedNodeId value) { var nodeid = (((NodeId)value)); if (value == null || nodeid == null || (!UseReversibleEncoding && NodeId.IsNull(value))) { WriteSimpleField(fieldName, null, quotes: false); return; } PushStructure(fieldName); string text = value.NamespaceUri; ushort namespaceIndex = value.NamespaceIndex; if (ForceNamespaceUri && text == null && namespaceIndex > ((!ForceNamespaceUriForIndex1) ? 1 : 0)) { text = Context.NamespaceUris.GetString(namespaceIndex); } WriteNodeIdContents(nodeid, text); uint num = value.ServerIndex; if (num >= 1) { string @string = m_context.ServerUris.GetString(num); if (!string.IsNullOrEmpty(@string)) { WriteSimpleField("ServerUri", @string, quotes: true); PopStructure(); return; } if (m_serverMappings != null && m_serverMappings.Length > num) { num = m_serverMappings[num]; } if (num != 0) { WriteUInt32("ServerUri", num); } } PopStructure(); } public void WriteStatusCode(string fieldName, StatusCode value) { if (fieldName != null && !IncludeDefaultValues && value == 0u) { WriteSimpleField(fieldName, null, quotes: false); } else if (UseReversibleEncoding) { WriteUInt32(fieldName, value.Code); } else if (value != 0u) { PushStructure(fieldName); WriteSimpleField("Code", value.Code.ToString(CultureInfo.InvariantCulture), quotes: false); WriteSimpleField("Symbol", StatusCode.LookupSymbolicId(value.CodeBits), quotes: true); PopStructure(); } } public void WriteDiagnosticInfo(string fieldName, DiagnosticInfo value) { WriteDiagnosticInfo(fieldName, value, 0); } public void WriteQualifiedName(string fieldName, QualifiedName value) { if (QualifiedName.IsNull(value)) { WriteSimpleField(fieldName, null, quotes: false); return; } PushStructure(fieldName); WriteString("Name", value.Name); WriteNamespaceIndex("Uri", value.NamespaceIndex); PopStructure(); } public void WriteLocalizedText(string fieldName, LocalizedText value) { if (LocalizedText.IsNullOrEmpty(value)) { WriteSimpleField(fieldName, null, quotes: false); } else if (UseReversibleEncoding) { PushStructure(fieldName); WriteSimpleField("Text", value.Text, quotes: true); if (!string.IsNullOrEmpty(value.Locale)) { WriteSimpleField("Locale", value.Locale, quotes: true); } PopStructure(); } else { WriteSimpleField(fieldName, value.Text, quotes: true); } } public void WriteVariant(string fieldName, Variant value) { if (Variant.Null == value) { WriteSimpleField(fieldName, null, quotes: false); return; } CheckAndIncrementNestingLevel(); try { bool flag = value.TypeInfo == null || value.TypeInfo.BuiltInType == BuiltInType.Null || value.Value == null; if (UseReversibleEncoding && !flag) { PushStructure(fieldName); byte value2 = (byte)value.TypeInfo.BuiltInType; if (value.TypeInfo.BuiltInType == BuiltInType.Enumeration) { value2 = 6; } WriteByte("Type", value2); fieldName = "Body"; } if (m_commaRequired) { m_writer.Write(","); } if (!string.IsNullOrEmpty(fieldName)) { m_writer.Write("\""); EscapeString(fieldName); m_writer.Write("\":"); } WriteVariantContents(value.Value, value.TypeInfo); if (UseReversibleEncoding && !flag) { Matrix matrix = value.Value as Matrix; if (matrix != null) { WriteInt32Array("Dimensions", matrix.Dimensions); } PopStructure(); } } finally { m_nestingLevel--; } } public void WriteDataValue(string fieldName, DataValue value) { if (value == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushStructure(fieldName); if (value != null) { if (value.WrappedValue.TypeInfo != null && value.WrappedValue.TypeInfo.BuiltInType != 0) { WriteVariant("Value", value.WrappedValue); } if (value.StatusCode != 0u) { WriteStatusCode("StatusCode", value.StatusCode); } if (value.SourceTimestamp != DateTime.MinValue) { WriteDateTime("SourceTimestamp", value.SourceTimestamp); if (value.SourcePicoseconds != 0) { WriteUInt16("SourcePicoseconds", value.SourcePicoseconds); } } if (value.ServerTimestamp != DateTime.MinValue) { WriteDateTime("ServerTimestamp", value.ServerTimestamp); if (value.ServerPicoseconds != 0) { WriteUInt16("ServerPicoseconds", value.ServerPicoseconds); } } } PopStructure(); } public void WriteExtensionObject(string fieldName, ExtensionObject value) { if (value == null || value.Encoding == ExtensionObjectEncoding.None) { WriteSimpleField(fieldName, null, quotes: false); return; } IEncodeable encodeable = value.Body as IEncodeable; if (!UseReversibleEncoding && encodeable != null) { IStructureTypeInfo structureTypeInfo = value.Body as IStructureTypeInfo; if (structureTypeInfo != null && structureTypeInfo.StructureType == StructureType.Union) { encodeable.Encode(this); return; } PushStructure(fieldName); encodeable.Encode(this); PopStructure(); return; } PushStructure(fieldName); ExpandedNodeId expandedNodeId = value.TypeId; if (encodeable != null) { expandedNodeId = value.Encoding switch { ExtensionObjectEncoding.Binary => encodeable.BinaryEncodingId, ExtensionObjectEncoding.Xml => encodeable.XmlEncodingId, _ => encodeable.TypeId, }; } NodeId value2 = ExpandedNodeId.ToNodeId(expandedNodeId, Context.NamespaceUris); if (UseReversibleEncoding) { WriteNodeId("TypeId", value2); } else { WriteExpandedNodeId("TypeId", expandedNodeId); } if (encodeable != null) { WriteEncodeable("Body", encodeable, null); } else if (value.Body != null) { if (value.Encoding == ExtensionObjectEncoding.Json) { WriteSimpleField("Body", value.Body as string, quotes: true); } else { WriteByte("Encoding", (byte)value.Encoding); if (value.Encoding == ExtensionObjectEncoding.Binary) { WriteByteString("Body", value.Body as byte[]); } else if (value.Encoding == ExtensionObjectEncoding.Xml) { WriteXmlElement("Body", value.Body as XmlElement); } } } PopStructure(); } public void WriteEncodeable(string fieldName, IEncodeable value, Type systemType) { if (value == null) { WriteSimpleField(fieldName, null, quotes: false); return; } if (m_nestingLevel == 0 && (m_commaRequired || m_topLevelIsArray) && (string.IsNullOrWhiteSpace(fieldName) ^ m_topLevelIsArray)) { throw ServiceResultException.Create(2147876864u, "With Array as top level, encodeables with fieldname will create invalid json"); } if (m_nestingLevel == 0 && !m_commaRequired && string.IsNullOrWhiteSpace(fieldName) && !m_topLevelIsArray) { m_writer.Flush(); if (m_writer.BaseStream.Length == 1) { m_writer.BaseStream.Seek(0L, SeekOrigin.Begin); } m_dontWriteClosing = true; } CheckAndIncrementNestingLevel(); try { PushStructure(fieldName); value?.Encode(this); PopStructure(); } finally { m_nestingLevel--; } } public void WriteEnumerated(string fieldName, Enum value) { int num = Convert.ToInt32(value, CultureInfo.InvariantCulture); string text = num.ToString(); if (UseReversibleEncoding) { WriteSimpleField(fieldName, text, quotes: false); return; } if (value.ToString() == text) { WriteSimpleField(fieldName, text, quotes: true); return; } WriteSimpleField(fieldName, Utils.Format("{0}_{1}", value.ToString(), num), quotes: true); } public void WriteEnumerated(string fieldName, int numeric) { string value = numeric.ToString(CultureInfo.InvariantCulture); WriteSimpleField(fieldName, value, !UseReversibleEncoding); } public void WriteBooleanArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteBoolean(null, values[i]); } PopArray(); } public void WriteSByteArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteSByte(null, values[i]); } PopArray(); } public void WriteByteArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteByte(null, values[i]); } PopArray(); } public void WriteInt16Array(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteInt16(null, values[i]); } PopArray(); } public void WriteUInt16Array(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteUInt16(null, values[i]); } PopArray(); } public void WriteInt32Array(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteInt32(null, values[i]); } PopArray(); } public void WriteUInt32Array(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteUInt32(null, values[i]); } PopArray(); } public void WriteInt64Array(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteInt64(null, values[i]); } PopArray(); } public void WriteUInt64Array(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteUInt64(null, values[i]); } PopArray(); } public void WriteFloatArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteFloat(null, values[i]); } PopArray(); } public void WriteDoubleArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteDouble(null, values[i]); } PopArray(); } public void WriteStringArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteString(null, values[i]); } PopArray(); } public void WriteDateTimeArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { if (values[i] <= DateTime.MinValue) { WriteSimpleField(null, null, quotes: false); } else { WriteDateTime(null, values[i]); } } PopArray(); } public void WriteGuidArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteGuid(null, values[i]); } PopArray(); } public void WriteGuidArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteGuid(null, values[i]); } PopArray(); } public void WriteByteStringArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteByteString(null, values[i]); } PopArray(); } public void WriteXmlElementArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteXmlElement(null, values[i]); } PopArray(); } public void WriteNodeIdArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteNodeId(null, values[i]); } PopArray(); } public void WriteExpandedNodeIdArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteExpandedNodeId(null, values[i]); } PopArray(); } public void WriteStatusCodeArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { if (!UseReversibleEncoding && values[i] == 0u) { WriteSimpleField(null, null, quotes: false); } else { WriteStatusCode(null, values[i]); } } PopArray(); } public void WriteDiagnosticInfoArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteDiagnosticInfo(null, values[i]); } PopArray(); } public void WriteQualifiedNameArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteQualifiedName(null, values[i]); } PopArray(); } public void WriteLocalizedTextArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteLocalizedText(null, values[i]); } PopArray(); } public void WriteVariantArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { if (values[i] == Variant.Null) { WriteSimpleField(null, null, quotes: false); } else { WriteVariant(null, values[i]); } } PopArray(); } public void WriteDataValueArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteDataValue(null, values[i]); } PopArray(); } public void WriteExtensionObjectArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int i = 0; i < values.Count; i++) { WriteExtensionObject(null, values[i]); } PopArray(); } public void WriteEncodeableArray(string fieldName, IList values, Type systemType) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); } else if (string.IsNullOrWhiteSpace(fieldName) && m_nestingLevel == 0 && !m_topLevelIsArray) { m_writer.Flush(); if (m_writer.BaseStream.Length == 1) { m_writer.BaseStream.Seek(0L, SeekOrigin.Begin); } m_nestingLevel++; PushArray(fieldName); for (int i = 0; i < values.Count; i++) { WriteEncodeable(null, values[i], systemType); } PopArray(); m_dontWriteClosing = true; m_nestingLevel--; } else { if (!string.IsNullOrWhiteSpace(fieldName) && m_nestingLevel == 0 && m_topLevelIsArray) { throw ServiceResultException.Create(2147876864u, "With Array as top level, encodeables array with filename will create invalid json"); } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } for (int j = 0; j < values.Count; j++) { WriteEncodeable(null, values[j], systemType); } PopArray(); } } public void WriteEnumeratedArray(string fieldName, Array values, Type systemType) { if (values == null || values.Length == 0) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Length) { throw new ServiceResultException(2148007936u); } Type elementType = values.GetType().GetElementType(); if (elementType.IsEnum) { foreach (Enum value in values) { WriteEnumerated(null, value); } } else { if (elementType != typeof(int)) { throw new ServiceResultException(2147876864u, Utils.Format("Type '{0}' is not allowed in an Enumeration.", elementType.FullName)); } foreach (int value2 in values) { WriteEnumerated(null, value2); } } PopArray(); } public void WriteVariantContents(object value, TypeInfo typeInfo) { try { m_inVariantWithEncoding = UseReversibleEncoding; if (value == null) { return; } m_commaRequired = false; if (typeInfo.ValueRank < 0) { switch (typeInfo.BuiltInType) { case BuiltInType.Boolean: WriteBoolean(null, (bool)value); break; case BuiltInType.SByte: WriteSByte(null, (sbyte)value); break; case BuiltInType.Byte: WriteByte(null, (byte)value); break; case BuiltInType.Int16: WriteInt16(null, (short)value); break; case BuiltInType.UInt16: WriteUInt16(null, (ushort)value); break; case BuiltInType.Int32: WriteInt32(null, (int)value); break; case BuiltInType.UInt32: WriteUInt32(null, (uint)value); break; case BuiltInType.Int64: WriteInt64(null, (long)value); break; case BuiltInType.UInt64: WriteUInt64(null, (ulong)value); break; case BuiltInType.Float: WriteFloat(null, (float)value); break; case BuiltInType.Double: WriteDouble(null, (double)value); break; case BuiltInType.String: WriteString(null, (string)value); break; case BuiltInType.DateTime: WriteDateTime(null, (DateTime)value); break; case BuiltInType.Guid: WriteGuid(null, (Uuid)value); break; case BuiltInType.ByteString: WriteByteString(null, (byte[])value); break; case BuiltInType.XmlElement: WriteXmlElement(null, (XmlElement)value); break; case BuiltInType.NodeId: WriteNodeId(null, (NodeId)value); break; case BuiltInType.ExpandedNodeId: WriteExpandedNodeId(null, (ExpandedNodeId)value); break; case BuiltInType.StatusCode: WriteStatusCode(null, (StatusCode)value); break; case BuiltInType.QualifiedName: WriteQualifiedName(null, (QualifiedName)value); break; case BuiltInType.LocalizedText: WriteLocalizedText(null, (LocalizedText)value); break; case BuiltInType.ExtensionObject: WriteExtensionObject(null, (ExtensionObject)value); break; case BuiltInType.DataValue: WriteDataValue(null, (DataValue)value); break; case BuiltInType.Enumeration: WriteEnumerated(null, (Enum)value); break; case BuiltInType.DiagnosticInfo: WriteDiagnosticInfo(null, (DiagnosticInfo)value); break; case BuiltInType.Variant: case BuiltInType.Number: case BuiltInType.Integer: case BuiltInType.UInteger: break; } } else { if (typeInfo.ValueRank < 1) { return; } int valueRank = typeInfo.ValueRank; if (UseReversibleEncoding) { Matrix matrix = value as Matrix; if (matrix != null) { value = matrix.Elements; valueRank = 1; } } WriteArray(null, value, valueRank, typeInfo.BuiltInType); return; } } finally { m_inVariantWithEncoding = false; } } public void WriteObjectArray(string fieldName, IList values) { if (values == null) { WriteSimpleField(fieldName, null, quotes: false); return; } PushArray(fieldName); if (values != null && m_context.MaxArrayLength > 0 && m_context.MaxArrayLength < values.Count) { throw new ServiceResultException(2148007936u); } if (values != null) { for (int i = 0; i < values.Count; i++) { WriteVariant("Variant", new Variant(values[i])); } } PopArray(); } public void WriteArray(string fieldName, object array, int valueRank, BuiltInType builtInType) { if (valueRank == 1) { switch (builtInType) { case BuiltInType.Boolean: WriteBooleanArray(fieldName, (bool[])array); break; case BuiltInType.SByte: WriteSByteArray(fieldName, (sbyte[])array); break; case BuiltInType.Byte: WriteByteArray(fieldName, (byte[])array); break; case BuiltInType.Int16: WriteInt16Array(fieldName, (short[])array); break; case BuiltInType.UInt16: WriteUInt16Array(fieldName, (ushort[])array); break; case BuiltInType.Int32: WriteInt32Array(fieldName, (int[])array); break; case BuiltInType.UInt32: WriteUInt32Array(fieldName, (uint[])array); break; case BuiltInType.Int64: WriteInt64Array(fieldName, (long[])array); break; case BuiltInType.UInt64: WriteUInt64Array(fieldName, (ulong[])array); break; case BuiltInType.Float: WriteFloatArray(fieldName, (float[])array); break; case BuiltInType.Double: WriteDoubleArray(fieldName, (double[])array); break; case BuiltInType.String: WriteStringArray(fieldName, (string[])array); break; case BuiltInType.DateTime: WriteDateTimeArray(fieldName, (DateTime[])array); break; case BuiltInType.Guid: WriteGuidArray(fieldName, (Uuid[])array); break; case BuiltInType.ByteString: WriteByteStringArray(fieldName, (byte[][])array); break; case BuiltInType.XmlElement: WriteXmlElementArray(fieldName, (XmlElement[])array); break; case BuiltInType.NodeId: WriteNodeIdArray(fieldName, (NodeId[])array); break; case BuiltInType.ExpandedNodeId: WriteExpandedNodeIdArray(fieldName, (ExpandedNodeId[])array); break; case BuiltInType.StatusCode: WriteStatusCodeArray(fieldName, (StatusCode[])array); break; case BuiltInType.QualifiedName: WriteQualifiedNameArray(fieldName, (QualifiedName[])array); break; case BuiltInType.LocalizedText: WriteLocalizedTextArray(fieldName, (LocalizedText[])array); break; case BuiltInType.ExtensionObject: WriteExtensionObjectArray(fieldName, (ExtensionObject[])array); break; case BuiltInType.DataValue: WriteDataValueArray(fieldName, (DataValue[])array); break; case BuiltInType.DiagnosticInfo: WriteDiagnosticInfoArray(fieldName, (DiagnosticInfo[])array); break; case BuiltInType.Enumeration: { Array array6 = array as Array; if (array6 == null) { throw ServiceResultException.Create(2147876864u, "Unexpected non Array type encountered while encoding an array of enumeration."); } WriteEnumeratedArray(fieldName, array6, array6.GetType().GetElementType()); break; } case BuiltInType.Variant: { Variant[] array3 = array as Variant[]; if (array3 != null) { WriteVariantArray(fieldName, array3); break; } IEncodeable[] array4 = array as IEncodeable[]; if (array4 != null) { WriteEncodeableArray(fieldName, array4, array.GetType().GetElementType()); break; } object[] array5 = array as object[]; if (array5 != null) { WriteObjectArray(fieldName, array5); break; } throw ServiceResultException.Create(2147876864u, "Unexpected type encountered while encoding an array of Variants: {0}", array.GetType()); } default: { IEncodeable[] array2 = array as IEncodeable[]; if (array2 != null) { WriteEncodeableArray(fieldName, array2, array.GetType().GetElementType()); break; } if (array == null) { WriteSimpleField(fieldName, null, quotes: false); break; } throw ServiceResultException.Create(2147876864u, "Unexpected BuiltInType encountered while encoding an array: {0}", builtInType); } } } else { if (valueRank <= 1) { return; } Matrix matrix = array as Matrix; if (matrix == null) { Array array7 = array as Array; if (array7 == null || array7.Rank != valueRank) { throw ServiceResultException.Create(2147876864u, "Unexpected array type encountered while encoding array: {0}", array.GetType().Name); } matrix = new Matrix(array7, builtInType); } if (matrix != null) { int index = 0; WriteStructureMatrix(fieldName, matrix, 0, ref index, matrix.TypeInfo); } } } private void WriteDiagnosticInfo(string fieldName, DiagnosticInfo value, int depth) { if (value == null || value.IsNullDiagnosticInfo) { WriteSimpleField(fieldName, null, quotes: false); return; } CheckAndIncrementNestingLevel(); try { PushStructure(fieldName); if (value.SymbolicId >= 0) { WriteSimpleField("SymbolicId", value.SymbolicId.ToString(CultureInfo.InvariantCulture), quotes: false); } if (value.NamespaceUri >= 0) { WriteSimpleField("NamespaceUri", value.NamespaceUri.ToString(CultureInfo.InvariantCulture), quotes: false); } if (value.Locale >= 0) { WriteSimpleField("Locale", value.Locale.ToString(CultureInfo.InvariantCulture), quotes: false); } if (value.LocalizedText >= 0) { WriteSimpleField("LocalizedText", value.LocalizedText.ToString(CultureInfo.InvariantCulture), quotes: false); } if (value.AdditionalInfo != null) { WriteSimpleField("AdditionalInfo", value.AdditionalInfo, quotes: true); } if (value.InnerStatusCode != 0u) { WriteStatusCode("InnerStatusCode", value.InnerStatusCode); } if (value.InnerDiagnosticInfo != null) { if (depth < DiagnosticInfo.MaxInnerDepth) { WriteDiagnosticInfo("InnerDiagnosticInfo", value.InnerDiagnosticInfo, depth + 1); } else { Utils.LogWarning("InnerDiagnosticInfo dropped because nesting exceeds maximum of {0}.", DiagnosticInfo.MaxInnerDepth); } } PopStructure(); } finally { m_nestingLevel--; } } private void WriteStructureMatrix(string fieldName, Matrix matrix, int dim, ref int index, TypeInfo typeInfo) { var (flag, num) = Matrix.ValidateDimensions(allowZeroDimension: true, matrix.Dimensions, Context.MaxArrayLength); if (!flag || num != matrix.Elements.Length) { throw new ArgumentException("The number of elements in the matrix does not match the dimensions."); } CheckAndIncrementNestingLevel(); try { int num2 = matrix.Dimensions[dim]; if (dim == matrix.Dimensions.Length - 1) { Array array = Array.CreateInstance(matrix.Elements.GetType().GetElementType(), num2); Array.Copy(matrix.Elements, index, array, 0, num2); if (m_commaRequired) { m_writer.Write(","); } WriteVariantContents(array, new TypeInfo(typeInfo.BuiltInType, 1)); index += num2; } else { PushArray(fieldName); for (int i = 0; i < num2; i++) { WriteStructureMatrix(null, matrix, dim + 1, ref index, typeInfo); } PopArray(); } } finally { m_nestingLevel--; } } private void CheckAndIncrementNestingLevel() { if (m_nestingLevel > m_context.MaxEncodingNestingLevels) { throw ServiceResultException.Create(2148007936u, "Maximum nesting level of {0} was exceeded", m_context.MaxEncodingNestingLevels); } m_nestingLevel++; } } }