PowerShell调用WSDL SOAP创建Kablink条目遇类型转换错误求助
这种看着完全矛盾的类型转换错误(明明是同类型却转不了)确实挺坑的,我之前处理过类似的PowerShell SOAP代理问题,给你拆解下原因和解决办法:
问题回顾
你用New-WebServiceProxy生成Kablink的SOAP代理后,手动创建了folder_addEntry.FolderEntry类型的$soapData对象,但调用folder_addEntry方法时却报错:
Cannot convert argument "entry", with value: "folder_addEntry.FolderEntry", for "folder_addEntry.FolderEntry" to type "folder_addEntry.FolderEntry": "Cannot convert the "folder_addEntry.FolderEntry" value of type "folder_addEntry.FolderEntry" to type "folder_addEntry.FolderEntry"."
核心原因
这本质是PowerShell中加载了重复的类型定义:New-WebServiceProxy在创建代理时会自动生成并加载对应的.NET程序集和类型,但你后续用New-Object -TypeName folder_addEntry.FolderEntry创建对象时,PowerShell可能从另一个上下文加载了同名但不同实例的类型(比如缓存的旧程序集),导致两个FolderEntry看似同名,实际来自不同程序集,触发类型不匹配。
解决步骤
1. 从代理的程序集中创建对象
不要手动指定类型名创建,而是直接从代理对象所在的程序集里获取类型并实例化,这样能保证类型完全匹配:
# 替换原来的$soapData创建代码 $folderEntryType = $kablinkConnector.GetType().Assembly.GetType("folder_addEntry.FolderEntry") $soapData = [Activator]::CreateInstance($folderEntryType)
这样生成的$soapData和代理方法期望的类型来自同一个程序集,就不会有转换问题了。
2. 清理冗余的对象初始化代码
看你脚本里有很多类似这样的代码:
$soapCustBool = New-Object ("folder_addEntry.CustomBooleanField") $soapCustBool = @()
这完全是多此一举——先创建了一个对象,立刻又把它覆盖成空数组。直接初始化空数组就行:
$soapCustBool = @() $soapCustDate = @() $soapCustEven = @() $soapCustLong = @() $soapCustStriArr = @() $soapCustStr = @()
既简洁,也避免了不必要的对象创建可能带来的类型冲突。
3. 验证方法参数签名
有时候参数顺序不对也会触发奇怪的错误,你可以先检查下folder_addEntry方法的参数定义:
$kablinkConnector | Get-Member -Name folder_addEntry
确认你传递的三个参数(空字符串、$soapData、空字符串)和方法要求的参数类型、顺序完全一致。
4. 重启PowerShell会话
如果之前多次运行脚本,PowerShell可能缓存了旧的程序集,导致类型冲突。关闭当前会话,重新打开一个干净的窗口再运行脚本,很多时候这种“玄学”错误就消失了。
修改后的完整脚本
$kablinkSoapUrl = "http://servername/ssr/secure/ws/TeamingServiceV1?wsdl" $kablinkConnector = New-WebServiceProxy -Uri $kablinkSoapUrl -Credential $Credential -namespace "folder_addEntry" #Set Binder ID to Shift Rota entries folder $binderID = 155 #Import Objects and Populate Default Values $soapDescription = New-Object ("folder_addEntry.Description") $soapDescription.text = "Some Description" $soapDescription.format = 0 $soapAttachment = New-Object("folder_addEntry.attachmentsField") $soapAttachment.name = "" $soapAttachment.type = "" $soapAttachment.attachments = @() $soapCreate = New-Object ("folder_addEntry.Timestamp") $soapCreate.date = Get-Date $soapCreate.principal = "" $soapCreate.principalId = 2 $soapModify = New-Object ("folder_addEntry.Timestamp") $soapModify.date = Get-Date $soapModify.principal = "" $soapModify.principalId = 2 $soapRating = New-Object ("folder_addEntry.AverageRating") $soapRating.averageRating = "" $soapRating.ratingCount = "" # 直接初始化空数组,无需多余操作 $soapCustBool = @() $soapCustDate = @() $soapCustEven = @() $soapCustLong = @() $soapCustStriArr = @() $soapCustStr = @() # 从代理程序集创建FolderEntry实例 $folderEntryType = $kablinkConnector.GetType().Assembly.GetType("folder_addEntry.FolderEntry") $soapData = [Activator]::CreateInstance($folderEntryType) #Configure Entry Properties $soapData.attachmentsField = $soapAttachment $soapData.averageRating = $soapRating $soapData.creation = $soapCreate $soapData.customBooleanFields = $soapCustBool $soapData.customDateFields = $soapCustDate $soapData.customEventFields = $soapCustEven $soapData.customLongArrayFields = $soapCustLong $soapData.customStringArrayFields = $soapCustStriArr $soapData.customStringFields = $soapCustStr $soapData.definitionId = "8a8ab38c62d40d5c0162fc8330eb01ad" $soapData.description = $soapDescription $soapData.entityType = "fileEntry" $soapData.eventAsIcalString = 0 $soapData.family = "file" $soapData.id = "" $soapData.modification = $soapModify $soapData.parentBinderId = 155 $soapData.permaLink = "" $soapData.title = "PowerShell Entry" $soapData.docLevel = 1 $soapData.docNumber = "" $soapData.href = "" $soapData.preDeleted = 0 $soapData.preDeletedBy = "" $soapData.preDeletedWhen = "" $soapData.reservedBy = "" $soapData.workflows = @() # Print Soap Data $soapData # Submit Entry $kablinkConnector.folder_addEntry("",$soapData,"")
额外小技巧
如果还是有问题,可以尝试去掉-namespace参数,让New-WebServiceProxy自动生成命名空间,然后用自动生成的命名空间来创建对象,也能避免手动指定命名空间带来的类型冲突。
内容的提问来源于stack exchange,提问作者Errkal

