You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Zeep使用疑问:是否必须构建嵌套字典,有无其他方案?

关于Zeep实现复杂SOAP请求的替代方案

问题描述

我有一个在Postman中测试通过的SOAP请求XML,现在需要在Python项目中用Zeep实现。我之前用过Java的SOAP工具(十多年前),刚接触Zeep。目前了解到的方式是构建嵌套字典传入Zeep Client,但想知道有没有其他更优雅的实现方式?

测试通过的XML示例

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope 
               xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:bsvc="urn:com.workday/bsvc"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <soapenv:Header>
        <wsse:Security soapenv:mustUnderstand="1">
            <wsse:UsernameToken>
                <wsse:Username>ISU_Get_Worker_Photo@mycompany_preview</wsse:Username>
                <wsse:Password
                           Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxxxxxxxxxx</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <bsvc:Change_Work_Contact_Information_Request bsvc:version="v42.0">
            <bsvc:Business_Process_Parameters>
                <bsvc:Auto_Complete>true</bsvc:Auto_Complete>
                <bsvc:Run_Now>true</bsvc:Run_Now>
            </bsvc:Business_Process_Parameters>
            <bsvc:Change_Work_Contact_Information_Data>
                <bsvc:Event_Effective_Date>2023-10-25</bsvc:Event_Effective_Date>
                <bsvc:Person_Reference>
                    <bsvc:ID bsvc:type="Employee_ID">9476</bsvc:ID>
                </bsvc:Person_Reference>
                <bsvc:Person_Contact_Information_Data>
                    <bsvc:Person_Phone_Information_Data bsvc:Replace_All="false">
                        <bsvc:Phone_Information_Data bsvc:Delete="false">
                            <bsvc:Phone_Data>
                                <bsvc:Device_Type_Reference bsvc:Descriptor="?">
                                    <bsvc:ID bsvc:type="Phone_Device_Type_ID">Landline</bsvc:ID>
                                </bsvc:Device_Type_Reference>
                                <bsvc:Country_Code_Reference bsvc:Descriptor="?">
                                    <bsvc:ID bsvc:type="Country_Phone_Code_ID">USA_1</bsvc:ID>
                                </bsvc:Country_Code_Reference>
                                <bsvc:Complete_Phone_Number>8444877900</bsvc:Complete_Phone_Number>
                                <bsvc:Extension></bsvc:Extension>
                            </bsvc:Phone_Data>
                            <bsvc:Usage_Data bsvc:Public="true">
                                <bsvc:Type_Data bsvc:Primary="true">
                                    <bsvc:Type_Reference bsvc:Descriptor="?">
                                        <bsvc:ID bsvc:type="Communication_Usage_Type_ID">Work</bsvc:ID>
                                    </bsvc:Type_Reference>
                                </bsvc:Type_Data>
                            </bsvc:Usage_Data>
                        </bsvc:Phone_Information_Data>
                    </bsvc:Person_Phone_Information_Data>
                </bsvc:Person_Contact_Information_Data>
            </bsvc:Change_Work_Contact_Information_Data>
        </bsvc:Change_Work_Contact_Information_Request>
    </soapenv:Body>
</soapenv:Envelope>

当前理解的实现方式

import zeep, os 
from zeep import Client 
from zeep.wsse.username import UsernameToken
    
hostname = 'host'
tenant_name = 'mycompany'
employee_id = '1234'
user = os.getenv('user') 
password = os.getenv('password') 
url = f'https://{hostname}.workday.com/ccx/service/{tenant_name}/Human_Resources/v36.2?wsdl'
    
client = Client(url, wsse=UsernameToken(user, password)) 

request_dict = { 
    # 嵌套字典对应所有XML标签
    "Business_Process_Parameters": {
        "Auto_Complete": True,
        "Run_Now": True
    },
    "Change_Work_Contact_Information_Data": {
        "Event_Effective_Date": "2023-10-25",
        "Person_Reference": {
            "ID": [{"value": "9476", "type": "Employee_ID"}]
        },
        # 其他嵌套字段...
    }
} 

client.service.Change_Work_Contact_Information_Request(request_dict)

我想知道有没有像简单示例那样的对象式调用方式,而不是全靠嵌套字典?


解决方案

不需要完全依赖嵌套字典,Zeep提供了多种更结构化的方式来构建复杂请求:

1. 使用类型工厂(Type Factory)构建强类型对象

Zeep会解析WSDL中的所有数据类型,你可以通过client.get_type()获取对应类型,然后创建对象并赋值,这种方式能利用WSDL的类型校验,减少错误。

示例代码:

import zeep, os
from zeep import Client
from zeep.wsse.username import UsernameToken

hostname = 'host'
tenant_name = 'mycompany'
user = os.getenv('user')
password = os.getenv('password')
url = f'https://{hostname}.workday.com/ccx/service/{tenant_name}/Human_Resources/v36.2?wsdl'

client = Client(url, wsse=UsernameToken(user, password))

# 获取请求所需的各类数据类型
BusinessProcessParams = client.get_type('ns0:Business_Process_ParametersType')
ChangeWorkContactData = client.get_type('ns0:Change_Work_Contact_Information_DataDataType')
PersonReference = client.get_type('ns0:RoleObjectIDType')
PhoneInformationData = client.get_type('ns0:Person_Phone_Information_DataType')
PhoneData = client.get_type('ns0:Phone_DataType')
DeviceTypeRef = client.get_type('ns0:Phone_Device_Type_ReferenceType')
CountryCodeRef = client.get_type('ns0:Country_Phone_Code_ReferenceType')
UsageData = client.get_type('ns0:Communication_Usage_Information_DataType')
TypeData = client.get_type('ns0:Communication_Usage_Type_DataType')
TypeRef = client.get_type('ns0:Communication_Usage_Type_ReferenceType')

# 逐层构建对象
business_params = BusinessProcessParams(Auto_Complete=True, Run_Now=True)

# 构建ID对象(Workday的ID通常为列表形式)
person_id = PersonReference(ID=[{'value': '9476', 'type': 'Employee_ID'}])
device_type_id = DeviceTypeRef(ID=[{'value': 'Landline', 'type': 'Phone_Device_Type_ID'}])
country_code_id = CountryCodeRef(ID=[{'value': 'USA_1', 'type': 'Country_Phone_Code_ID'}])
type_ref = TypeRef(ID=[{'value': 'Work', 'type': 'Communication_Usage_Type_ID'}])

type_data = TypeData(Type_Reference=type_ref, Primary=True)
usage_data = UsageData(Type_Data=[type_data], Public=True)

phone_data = PhoneData(
    Device_Type_Reference=device_type_id,
    Country_Code_Reference=country_code_id,
    Complete_Phone_Number='8444877900',
    Extension=''
)

phone_info_data = PhoneInformationData(
    Phone_Data=phone_data,
    Usage_Data=usage_data,
    Delete=False
)

person_phone_data = client.get_type('ns0:Person_Phone_Information_DataType')(
    Phone_Information_Data=[phone_info_data],
    Replace_All=False
)

contact_data = ChangeWorkContactData(
    Event_Effective_Date='2023-10-25',
    Person_Reference=person_id,
    Person_Contact_Information_Data={
        'Person_Phone_Information_Data': person_phone_data
    }
)

# 发起请求
response = client.service.Change_Work_Contact_Information_Request(
    Business_Process_Parameters=business_params,
    Change_Work_Contact_Information_Data=contact_data,
    version='v42.0'
)

2. 混合使用关键字参数与嵌套字典

对于复杂层级,可以直接传入关键字参数,嵌套部分用字典,兼顾简洁与可读性:

import zeep, os
from zeep import Client
from zeep.wsse.username import UsernameToken

hostname = 'host'
tenant_name = 'mycompany'
user = os.getenv('user')
password = os.getenv('password')
url = f'https://{hostname}.workday.com/ccx/service/{tenant_name}/Human_Resources/v36.2?wsdl'

client = Client(url, wsse=UsernameToken(user, password))

# 直接用关键字参数传递顶层字段,嵌套部分用字典
response = client.service.Change_Work_Contact_Information_Request(
    version='v42.0',
    Business_Process_Parameters={
        'Auto_Complete': True,
        'Run_Now': True
    },
    Change_Work_Contact_Information_Data={
        'Event_Effective_Date': '2023-10-25',
        'Person_Reference': {
            'ID': [{'value': '9476', 'type': 'Employee_ID'}]
        },
        'Person_Contact_Information_Data': {
            'Person_Phone_Information_Data': {
                'Replace_All': False,
                'Phone_Information_Data': [{
                    'Delete': False,
                    'Phone_Data': {
                        'Device_Type_Reference': {
                            'ID': [{'value': 'Landline', 'type': 'Phone_Device_Type_ID'}]
                        },
                        'Country_Code_Reference': {
                            'ID': [{'value': 'USA_1', 'type': 'Country_Phone_Code_ID'}]
                        },
                        'Complete_Phone_Number': '8444877900',
                        'Extension': ''
                    },
                    'Usage_Data': {
                        'Public': True,
                        'Type_Data': [{
                            'Primary': True,
                            'Type_Reference': {
                                'ID': [{'value': 'Work', 'type': 'Communication_Usage_Type_ID'}]
                            }
                        }]
                    }
                }]
            }
        }
    }
)

3. 直接发送原始XML(应急可用,不推荐)

如果不想重构请求结构,可以通过Zeep的transport直接发送原始XML,但会失去Zeep的类型校验和WSDL解析优势:

import zeep, os
from zeep import Client, Transport
from zeep.wsse.username import UsernameToken
import requests

hostname = 'host'
tenant_name = 'mycompany'
user = os.getenv('user')
password = os.getenv('password')
url = f'https://{hostname}.workday.com/ccx/service/{tenant_name}/Human_Resources/v36.2'

# 替换变量后的原始XML
raw_xml = """<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope 
               xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:bsvc="urn:com.workday/bsvc"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <soapenv:Header>
        <wsse:Security soapenv:mustUnderstand="1">
            <wsse:UsernameToken>
                <wsse:Username>{user}</wsse:Username>
                <wsse:Password
                           Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">{password}</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <bsvc:Change_Work_Contact_Information_Request bsvc:version="v42.0">
            <bsvc:Business_Process_Parameters>
                <bsvc:Auto_Complete>true</bsvc:Auto_Complete>
                <bsvc:Run_Now>true</bsvc:Run_Now>
            </bsvc:Business_Process_Parameters>
            <bsvc:Change_Work_Contact_Information_Data>
                <bsvc:Event_Effective_Date>2023-10-25</bsvc:Event_Effective_Date>
                <bsvc:Person_Reference>
                    <bsvc:ID bsvc:type="Employee_ID">9476</bsvc:ID>
                </bsvc:Person_Reference>
                <bsvc:Person_Contact_Information_Data>
                    <bsvc:Person_Phone_Information_Data bsvc:Replace_All="false">
                        <bsvc:Phone_Information_Data bsvc:Delete="false">
                            <bsvc:Phone_Data>
                                <bsvc:Device_Type_Reference bsvc:Descriptor="?">
                                    <bsvc:ID bsvc:type="Phone_Device_Type_ID">Landline</bsvc:ID>
                                </bsvc:Device_Type_Reference>
                                <bsvc:Country_Code_Reference bsvc:Descriptor="?">
                                    <bsvc:ID bsvc:type="Country_Phone_Code_ID">USA_1</bsvc:ID>
                                </bsvc:Country_Code_Reference>
                                <bsvc:Complete_Phone_Number>8444877900</bsvc:Complete_Phone_Number>
                                <bsvc:Extension></bsvc:Extension>
                            </bsvc:Phone_Data>
                            <bsvc:Usage_Data bsvc:Public="true">
                                <bsvc:Type_Data bsvc:Primary="true">
                                    <bsvc:Type_Reference bsvc:Descriptor="?">
                                        <bsvc:ID bsvc:type="Communication_Usage_Type_ID">Work</bsvc:ID>
                                    </bsvc:Type_Reference>
                                </bsvc:Type_Data>
                            </bsvc:Usage_Data>
                        </bsvc:Phone_Information_Data>
                    </bsvc:Person_Phone_Information_Data>
                </bsvc:Person_Contact_Information_Data>
            </bsvc:Change_Work_Contact_Information_Data>
        </bsvc:Change_Work_Contact_Information_Request>
    </soapenv:Body>
</soapenv:Envelope>""".format(user=user, password=password)

# 构建transport并发送请求
transport = Transport(session=requests.Session())
response = transport.post(
    url,
    raw_xml,
    headers={'Content-Type': 'text/xml; charset=utf-8'}
)

# 解析响应
parsed_response = client.create_message(client.service, 'Change_Work_Contact_Information_Request', envelope=response.content)

内容的提问来源于stack exchange,提问作者jb62

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:45:58