跳转至

发送和接收 JSON

在本教程中,我们将解释如何发送和接收 JSON 消息。

TCP 和 WebSocket

使用 JSON 时,您可以选择使用 TCP 连接或 WebSocket 连接。

发送 JSON

如果您选择在集成中使用 JSON,您需要向 cTrader 后端发送包含有效 JSON 的字符串。 这些字符串中的 JSON 对象必须包含以下键。

值数据类型 定义
"clientMsgId" 字符串 消息的唯一 ID。 此 ID 必须在客户端生成并分配。
"payloadType" 整数 表示消息的整数。 有效负载类型的完整列表包含在 ProtoOAPayloadType 中。
"payload" JSON 对象 包含消息实际内容的嵌套 JSON 对象。

一个有效字符串的示例(表示 ProtoOAApplicationAuthReq 消息)可以在下面找到。

"{"clientMsgId": "cm_id_2", "payloadType": 2100, "payload": {"clientId": "34Rsd_T098asHkl","clientSecret": "validClientSecret"}}"

要发送正确的 JSON 字符串,您需要在您选择的编程语言中创建一个自定义的序列化机制。

在下面的示例中,我们定义了几种处理序列化的方法。 您可以根据您偏好的编程语言和设计模式实现自己的方法。

下面,我们定义了两个基类,然后创建了 ProtoOAApplicationAuthReq 消息的自定义表示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public abstract class OpenAPIMessageBase
{
    public string ClientMsgId { get; set; }
    public abstract int PayloadType { get; }
}

public abstract class OpenAPIMessagePayloadBase { }

public class ApplicationAuthReq : OpenAPIMessageBase
{
    public ApplicationAuthReq() { }
    public ApplicationAuthReq(string clientId, string clientSecret)
    {
        this.Payload = new ApplicationAuthReqPayload(clientId, clientSecret);
        this.ClientMsgId = Guid.NewGuid().ToString();
    }

    public override int PayloadType => 2100;
    public ApplicationAuthReqPayload? Payload { get; set; }

}

public class ApplicationAuthReqPayload : OpenAPIMessagePayloadBase
{
    public ApplicationAuthReqPayload() { }
    public ApplicationAuthReqPayload(string clientId, string clientSecret)
    {
        this.ClientId = clientId;
        this.ClientSecret = clientSecret;
    }
    public string ClientId { get; set; } = string.Empty;
    public string ClientSecret { get; set; } = string.Empty;
}

我们为所有 Open API 消息创建了一个基类,并出于演示目的定义了 ProtoOAApplicationAuthReq 的类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import uuid
import json

class OpenAPIMessage:

def payloadType(self):
    pass

def payload(self):
    pass

def clientMsgId(self):
    pass

def asJsonString(self):
    pass


class ApplicationAuthReq(OpenAPIMessage):

def __init__(self, clientId, clientSecret, clientMsgId = str(uuid.uuid4())):
    self.clientId = clientId
    self.clientSecret = client_secret
    self.payloadType = 2100
    self.clientMsgId = clientMsgId
    self.payload = {"clientId": self.clientId, "clientSecret": self.clientSecret}



def asJsonString(self):
    return json.dumps({"clientMsgId": self.clientMsgId, "payloadType": self.payloadType, "payload": self.payload})

在实现所需逻辑后,您应该能够通过创建表示这些消息的类的新实例并将其传递给您偏好的 TCP 或 WebSocket 客户端来向 cTrader 后端发送新消息。

接收 JSON

要接收和处理 JSON 消息,您需要实现一种将 JSON 字符串反序列化为 Open API 消息表示的方法。

要实现此方法,您可能会发现初始化一个映射(或字典)很有帮助,其中负载类型可以作为键,表示消息的类名称作为值。 出于异常处理的目的,您可能希望验证您收到的消息的负载类型是否在此映射或字典中。 如果存在这样的键,您可以使用任何合适的逻辑反序列化消息。

在实现此方法后,您可以将其用作每次收到新消息时触发的回调。

请注意,在下面的示例中 TMessage 是一个泛型类型。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public static class MessageReceivedCallbacks
{
    public static Dictionary<int, Type> messages = new Dictionary<int, Type>
    {
        {2100, typeof(ApplicationAuthReq) },

    };

    public static TMessage ReadMessage<TMessage>(string jsonString) where TMessage : OpenAPIMessageBase
    {
        JsonDocument doc = JsonDocument.Parse(jsonString);
        int payloadType = doc.RootElement.GetProperty("payloadType").GetInt32();

        if (!messages.TryGetValue(payloadType, out var type))
            throw new Exception("This payload type is not supported");

        var result = JsonSerializer.Deserialize(jsonString, type)  as TMessage;

        return result;
    }
}

向下转型

ReadMessage() 方法实际上并不返回特定的消息类;您需要在接收消息时单独处理向下转型。

我们向 ApplicationAuthReq 类添加了以下方法。

1
2
3
@staticmethod
def fromJson(jsonDct):
    return ApplicationAuthReq(clientId=jsonDct['payload']['clientId'], clientSecret=jsonDct['payload']['clientSecret'], clientMsgId=jsonDct['clientMsgId'])

然后我们添加了 readMessage 回调并从字典中检索负载。

1
2
3
4
5
6
7
8
9
class MessageReceivedCallbacks:

messagesDict = {2100: ApplicationAuthReq}

@staticmethod
def readMessage(jsonString):
    json_dct = json.loads(jsonString)
    payloadType = jsonDct['payloadType']
    return MessageReceivedCallbacks.messagesDict[payloadType].fromJson(jsonDct)

向下转型

由于 Python 是鸭子类型的,因此无需单独处理向下转型。 您可以在接收 JSON 字符串时将其反序列化为所需的类型。