private static void WriteObjectParameterValue(BinaryWriter writer,
                                                      ParameterMetadata parameterMetadata, JToken parameter)
        {
            var obj = (JObject)(parameter ?? new JObject());

            foreach (var childParameterMetadata in parameterMetadata.Children)
            {
                var childParameter = obj[childParameterMetadata.Name];
                WriteParameterValue(writer, childParameterMetadata, childParameter);
            }
        }
        private static void WriteArrayParameterValue(BinaryWriter writer,
                                                     ParameterMetadata parameterMetadata, JToken parameter)
        {
            if (parameter == null)
            {
                writer.Write(0);
                return;
            }

            var array = (JArray)parameter;

            writer.Write((ushort)array.Count);

            foreach (var item in array)
            {
                WriteParameterValue(writer, parameterMetadata.Children[0], item);
            }
        }
 private static void WriteObjectParameterValue(BinaryWriter writer,
     ParameterMetadata parameterMetadata, JToken parameter)
 {
     var obj = (JObject) (parameter ?? new JObject());
     foreach (var childParameterMetadata in parameterMetadata.Children)
     {
         var childParameter = obj[childParameterMetadata.Name];
         WriteParameterValue(writer, childParameterMetadata, childParameter);
     }
 }
        private static void WriteArrayParameterValue(BinaryWriter writer,
            ParameterMetadata parameterMetadata, JToken parameter)
        {
            if (parameter == null)
            {
                writer.Write(0);
                return;
            }

            var array = (JArray) parameter;
            writer.Write((ushort) array.Count);

            foreach (var item in array)
                WriteParameterValue(writer, parameterMetadata.Children[0], item);
        }
        private static void WriteParameterValue(BinaryWriter writer,
            ParameterMetadata parameterMetadata, JToken parameter)
        {
            switch (parameterMetadata.DataType)
            {
                case DataType.Null:
                    break;

                case DataType.Byte:
                    writer.Write((byte) (parameter ?? 0));
                    break;

                case DataType.Word:
                    writer.Write((ushort) (parameter ?? 0));
                    break;

                case DataType.Dword:
                    writer.Write((uint) (parameter ?? 0));
                    break;

                case DataType.Qword:
                    writer.Write((ulong) (parameter ?? 0));
                    break;

                case DataType.SignedByte:
                    writer.Write((sbyte) (parameter ?? 0));
                    break;

                case DataType.SignedWord:
                    writer.Write((short) (parameter ?? 0));
                    break;

                case DataType.SignedDword:
                    writer.Write((int) (parameter ?? 0));
                    break;

                case DataType.SignedQword:
                    writer.Write((long) (parameter ?? 0));
                    break;

                case DataType.Single:
                    writer.Write((float) (parameter ?? 0));
                    break;

                case DataType.Double:
                    writer.Write((double) (parameter ?? 0));
                    break;

                case DataType.Boolean:
                    var boolValue = (bool) (parameter ?? false);
                    writer.Write((byte) (boolValue ? 1 : 0));
                    break;

                case DataType.Guid:
                    writer.WriteGuid((Guid) (parameter ?? Guid.Empty));
                    break;

                case DataType.UtfString:
                    writer.WriteUtfString((string) (parameter ?? string.Empty));
                    break;

                case DataType.Binary:
                    writer.WriteBinary((byte[]) (parameter ?? new byte[0]));
                    break;

                case DataType.Array:
                    WriteArrayParameterValue(writer, parameterMetadata, parameter);
                    break;

                case DataType.Object:
                    WriteObjectParameterValue(writer, parameterMetadata, parameter);
                    break;

                default:
                    throw new NotSupportedException(parameterMetadata.DataType + " is not supported for parameters");
            }
        }
        private static JToken ReadParameterValue(BinaryReader reader, ParameterMetadata parameterMetadata)
        {
            switch (parameterMetadata.DataType)
            {
                case DataType.Null:
                    return null;

                case DataType.Byte:
                    return (int) reader.ReadByte();
                
                case DataType.Word:
                    return reader.ReadUInt16();

                case DataType.Dword:
                    return reader.ReadUInt32();

                case DataType.Qword:
                    return reader.ReadUInt64();

                case DataType.SignedByte:
                    return reader.ReadSByte();

                case DataType.SignedWord:
                    return reader.ReadInt16();

                case DataType.SignedDword:
                    return reader.ReadInt32();
                
                case DataType.SignedQword:
                    return reader.ReadInt64();

                case DataType.Single:
                    return reader.ReadSingle();

                case DataType.Double:
                    return reader.ReadDouble();

                case DataType.Boolean:
                    return reader.ReadByte() != 0;

                case DataType.Guid:
                    return reader.ReadGuid();

                case DataType.UtfString:
                    return reader.ReadUtfString();

                case DataType.Binary:
                    return reader.ReadBinary();

                case DataType.Array:
                    return new JArray(reader
                        .ReadArray(r => ReadParameterValue(r, parameterMetadata.Children[0]))
                        .Cast<object>()
                        .ToArray());

                case DataType.Object:
                    return new JObject(parameterMetadata.Children
                        .Select(p => new JProperty(p.Name, ReadParameterValue(reader, p)))
                        .Cast<object>()
                        .ToArray());

                default:
                    throw new NotSupportedException(parameterMetadata.DataType + " is not supported for parameters");
            }
        }
        private static void WriteParameterValue(BinaryWriter writer,
                                                ParameterMetadata parameterMetadata, JToken parameter)
        {
            switch (parameterMetadata.DataType)
            {
            case DataType.Null:
                break;

            case DataType.Byte:
                writer.Write((byte)(parameter ?? 0));
                break;

            case DataType.Word:
                writer.Write((ushort)(parameter ?? 0));
                break;

            case DataType.Dword:
                writer.Write((uint)(parameter ?? 0));
                break;

            case DataType.Qword:
                writer.Write((ulong)(parameter ?? 0));
                break;

            case DataType.SignedByte:
                writer.Write((sbyte)(parameter ?? 0));
                break;

            case DataType.SignedWord:
                writer.Write((short)(parameter ?? 0));
                break;

            case DataType.SignedDword:
                writer.Write((int)(parameter ?? 0));
                break;

            case DataType.SignedQword:
                writer.Write((long)(parameter ?? 0));
                break;

            case DataType.Single:
                writer.Write((float)(parameter ?? 0));
                break;

            case DataType.Double:
                writer.Write((double)(parameter ?? 0));
                break;

            case DataType.Boolean:
                var boolValue = (bool)(parameter ?? false);
                writer.Write((byte)(boolValue ? 1 : 0));
                break;

            case DataType.Guid:
                writer.WriteGuid((Guid)(parameter ?? Guid.Empty));
                break;

            case DataType.UtfString:
                writer.WriteUtfString((string)(parameter ?? string.Empty));
                break;

            case DataType.Binary:
                writer.WriteBinary((byte[])(parameter ?? new byte[0]));
                break;

            case DataType.Array:
                WriteArrayParameterValue(writer, parameterMetadata, parameter);
                break;

            case DataType.Object:
                WriteObjectParameterValue(writer, parameterMetadata, parameter);
                break;

            default:
                throw new NotSupportedException(parameterMetadata.DataType + " is not supported for parameters");
            }
        }
        private static JToken ReadParameterValue(BinaryReader reader, ParameterMetadata parameterMetadata)
        {
            switch (parameterMetadata.DataType)
            {
            case DataType.Null:
                return(null);

            case DataType.Byte:
                return((int)reader.ReadByte());

            case DataType.Word:
                return(reader.ReadUInt16());

            case DataType.Dword:
                return(reader.ReadUInt32());

            case DataType.Qword:
                return(reader.ReadUInt64());

            case DataType.SignedByte:
                return(reader.ReadSByte());

            case DataType.SignedWord:
                return(reader.ReadInt16());

            case DataType.SignedDword:
                return(reader.ReadInt32());

            case DataType.SignedQword:
                return(reader.ReadInt64());

            case DataType.Single:
                return(reader.ReadSingle());

            case DataType.Double:
                return(reader.ReadDouble());

            case DataType.Boolean:
                return(reader.ReadByte() != 0);

            case DataType.Guid:
                return(reader.ReadGuid());

            case DataType.UtfString:
                return(reader.ReadUtfString());

            case DataType.Binary:
                return(reader.ReadBinary());

            case DataType.Array:
                return(new JArray(reader
                                  .ReadArray(r => ReadParameterValue(r, parameterMetadata.Children[0]))
                                  .Cast <object>()
                                  .ToArray()));

            case DataType.Object:
                return(new JObject(parameterMetadata.Children
                                   .Select(p => new JProperty(p.Name, ReadParameterValue(reader, p)))
                                   .Cast <object>()
                                   .ToArray()));

            default:
                throw new NotSupportedException(parameterMetadata.DataType + " is not supported for parameters");
            }
        }
 /// <summary>
 /// Initialize new instance of <see cref="ParameterMetadata"/>
 /// </summary>
 public ParameterMetadata(string name, DataType dataType, ParameterMetadata[] children = null)
 {
     Name = name;
     Children = children;
     DataType = dataType;
 }