从 SOAP 响应中获取 JSON

Posted

技术标签:

【中文标题】从 SOAP 响应中获取 JSON【英文标题】:Getting JSON From SOAP Response 【发布时间】:2018-05-21 08:52:04 【问题描述】:

我是向肥皂 API 发出请求并从中提取数据的新手。我想知道如何从这个 Soap 响应中解析 Json 数据:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope   xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>["nationalid":"2135199186","countrylist":["countryCode":"AFG","CountryName":"Afghanistan","countryCode":"DZA","CountryName":"Algeria","countryCode":"BHR","CountryName":"Bahrain","countryCode":"COM","CountryName":"Comoros","countryCode":"DJI","CountryName":"Djibouti","countryCode":"EGY","CountryName":"Egypt","countryCode":"ERI","CountryName":"Eritrea","countryCode":"IRQ","CountryName":"Iraq","countryCode":"JOR","CountryName":"Jordan","countryCode":"KWT","CountryName":"Kuwait","countryCode":"LBN","CountryName":"Lebanon","countryCode":"LBY","CountryName":"Libyan Arab Jamahiriya","countryCode":"MRT","CountryName":"Mauritania","countryCode":"MAR","CountryName":"Morocco","countryCode":"OMN","CountryName":"Oman","countryCode":"PAK","CountryName":"Pakistan","countryCode":"PSE","CountryName":"Palestinian Territory, Occupie","countryCode":"QAT","CountryName":"Qatar","countryCode":"SOM","CountryName":"Somalia","countryCode":"SDN","CountryName":"Sudan","countryCode":"SYR","CountryName":"Syrian Arab Republic","countryCode":"TUN","CountryName":"Tunisia","countryCode":"ARE","CountryName":"United Arab Emirates","countryCode":"YEM","CountryName":"Yemen"],"isError":false]</soapenv:Body>
</soapenv:Envelope>

编辑:

请求包如下所示:

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:name="http://www.example.org/Name">
    <soapenv:Header><wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   <wsse:UsernameToken> 
   <wsse:Username>UODA_GUEST</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">@password123</wsse:Password>
   </wsse:UsernameToken>
 </wsse:Security>
   </soapenv:Header> 
   <soapenv:Body>
      <Name>
         <NATIONAL_ID>2135199186</NATIONAL_ID>
         <SEC_KEY>1adb445f8815e450b25addad899cab156e63088c</SEC_KEY>
         <LANG_CODE>ENG</LANG_CODE>
         <SEC_CODE>@password123</SEC_CODE>
      </Name>
   </soapenv:Body>
</soapenv:Envelope>

对于 api 网址:http://pscstestserver.iau.edu.sa:8888/PSIGW/PeopleSoftServiceListeningConnector/U_COUNTRYLIST_ADM.1.wsdl

响应如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope   xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>["nationalid":"2135199186","countrylist":["countryCode":"AFG","CountryName":"Afghanistan","countryCode":"DZA","CountryName":"Algeria","countryCode":"BHR","CountryName":"Bahrain","countryCode":"COM","CountryName":"Comoros","countryCode":"DJI","CountryName":"Djibouti","countryCode":"EGY","CountryName":"Egypt","countryCode":"ERI","CountryName":"Eritrea","countryCode":"IRQ","CountryName":"Iraq","countryCode":"JOR","CountryName":"Jordan","countryCode":"KWT","CountryName":"Kuwait","countryCode":"LBN","CountryName":"Lebanon","countryCode":"LBY","CountryName":"Libyan Arab Jamahiriya","countryCode":"MRT","CountryName":"Mauritania","countryCode":"MAR","CountryName":"Morocco","countryCode":"OMN","CountryName":"Oman","countryCode":"PAK","CountryName":"Pakistan","countryCode":"PSE","CountryName":"Palestinian Territory, Occupie","countryCode":"QAT","CountryName":"Qatar","countryCode":"SOM","CountryName":"Somalia","countryCode":"SDN","CountryName":"Sudan","countryCode":"SYR","CountryName":"Syrian Arab Republic","countryCode":"TUN","CountryName":"Tunisia","countryCode":"ARE","CountryName":"United Arab Emirates","countryCode":"YEM","CountryName":"Yemen"],"isError":false]</soapenv:Body>
</soapenv:Envelope>

我正在创建这样的请求:

        static func createServiceRequest(params : Dictionary<String,String>, serviceName:String, urlString: String, method: String) -> URLRequest 
        var parameters : String = ""
        for (key, param) in params 
            parameters = "\(parameters)<\(key)>\(param)</\(key)>"
         
        let soapMessage =  "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\"><Body><\(serviceName) xmlns=\"http://ud.edu.sa/api/ldap\">\(parameters)</\(serviceName)></Body></Envelope>"
        let soapLenth = String(soapMessage.count)
        let theURL = URL(string: urlString)
        var mutableR = URLRequest(url: theURL!) 
        // MUTABLE REQUEST
        mutableR.addValue("text/xml; charset=utf-8", forHTTPHeaderField: "Content-Type")
        mutableR.addValue("text/html; charset=utf-8", forHTTPHeaderField: "Content-Type")
        mutableR.addValue(soapLenth, forHTTPHeaderField: "Content-Length")
        mutableR.httpMethod = method
        mutableR.httpBody = soapMessage.data(using: String.Encoding.utf8)
        return mutableR
    

然后创建一个 url 会话并使用 Stimorol 的 XMLElementExtractor 类:

    func fetchJsonFor(request: URLRequest, completion: @escaping ((Any?) -> Void)) 
    let task = URLSession.shared.dataTask(with: request)  (data, response, error) in
        if error != nil
            print(error?.localizedDescription ?? "Error")
            completion(nil)
        
        guard let data = data else
            completion(nil)
            return
        
        let jsonString = XMLElementExtractor.extractElement("soapenv:Body", fromXML: data) ?? "NO JSON STRING"
        print("JSON STRING:") 
        print(jsonString)
    
    task.resume()

但我得到 jsonString 没有显示任何 jsonString.count = 1

编辑:

print(String(data: data, encoding: .utf8) ?? "NO UTF8 DATA")

将打印此屏幕截图中的文本:

显然,我需要做额外的步骤来执行这一行

<wsdl:operation name="U_COUNTRYLIST"

因为当我单击屏幕截图上角的 U_COUNTRYLIST 按钮时,会出现另一个屏幕。然后,当我将整个请求数据包粘贴到该屏幕中时,我会得到带有所需 json 的 end soap 响应。

【问题讨论】:

你能提供String(data: data, encoding: .utf8)的结果吗? 感谢帮助,显然这将打印可能需要一些工作的 xml 数据。我更新了问题以说明这一点。 所以你需要提出另一个请求。查询 wsdl 操作需要更高级的解析器。 【参考方案1】:

你可以使用这个 XML 解析 Like this one AEXML

检查一下

print(String(describing: xmlDoc.root["soapenv:Body"].value))

这里是代码

import UIKit
import AEXML

class ViewController: UIViewController 


    override func viewDidLoad() 
        super.viewDidLoad()

        // example from README.md
        guard
            let xmlPath = Bundle.main.path(forResource: "example", ofType: "xml"),
            let data = try? Data(contentsOf: URL(fileURLWithPath: xmlPath))
        else 
            print("resource not found!")
            return
        

        // example of using NSXMLParserOptions
        var options = AEXMLOptions()
        options.parserSettings.shouldProcessNamespaces = false
        options.parserSettings.shouldReportNamespacePrefixes = false
        options.parserSettings.shouldResolveExternalEntities = false

        do 
            let xmlDoc = try AEXMLDocument(xml: data, options: options)

            // prints the same XML structure as original
            print(xmlDoc.xml)

            // prints cats, dogs
            for child in xmlDoc.root.children 
                print(child.name)
            

          print(String(describing: xmlDoc.root["soapenv:Body"].value)) // Print your REsponse


        
        catch 
            print("\(error)")
        
    

【讨论】:

【参考方案2】:

如果您不想要外部依赖,而您唯一需要的 XML 元素就是这个 Body,您可以为此制作简单的解析器:

import Foundation

final class XMLElementExtractor: NSObject, XMLParserDelegate 
    private var isTarget = false
    private var value: String?
    private let elementName: String

    private init(elementName: String) 
        self.elementName = elementName
    

    static func extractElement(_ elementName: String, fromXML data: Data) -> String? 
        let extractor = XMLElementExtractor(elementName: elementName)
        let parser = XMLParser(data: data)
        parser.delegate = extractor
        parser.parse()
        return extractor.value
    

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) 
        isTarget = elementName == self.elementName
    

    func parser(_ parser: XMLParser, foundCharacters string: String) 
        if isTarget 
            value = value?.appending(string) ?? string
        
    

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) 
        if elementName == self.elementName 
            parser.abortParsing()
        
    


let jsonString = XMLElementExtractor.extractElement("soapenv:Body", fromXML: data)

然后只需使用CodableJSONSerialization 解析返回的字符串。

【讨论】:

我真的希望这能奏效,但我得到一个什么都没有显示的 jsonString 这很奇怪,因为我使用您的示例和let data = xmlString.data(using: .utf8) 得到了正文中包含的确切内容。你要传递给解析器什么?

以上是关于从 SOAP 响应中获取 JSON的主要内容,如果未能解决你的问题,请参考以下文章

如何从 PHP 中的以下 SOAP XML 响应中获取 pinBlocked 标记

如何在php中解析soap xml响应并从字符串中获取信息

C# - 如何从响应中获取值

如何从 SOAP 响应中检索元素值

SOAP UI:获取响应时出错;尝试连接到 Microsoft SQL Server 时为 null

在 iOS 中解析 JSON 并将结果存储到 Array