猿问

有没有更简洁的方法来实现 NHS 数据从 XML 到 JSON 的转换

我正在使用来自公共 NHS API 的位置和地址数据,它以 XML 格式输入,我已将其转换为 JSON 以在我的 web 应用程序中使用。


我的代码正在运行并产生所需的结果,但是我不禁对这段代码中嵌套的 foreach 循环的数量感到畏缩。


有没有更干净的方法来实现相同的结果,并且性能更高?


我曾尝试使用 simplexml_load_string 和许多变体,但它拒绝输出/解析包含 s:organisationSummary 数据的嵌套元素的内容。


这是 PHP 类


class XmlElement {

    public $name;

    public $attributes;

    public $content;

    public $children;

}


class GpService

{

    protected $apiKey;


    public function __construct()

    {

        $this->apiKey = env('NHS_API_KEY');

    }


    public function xmlToObject($xml) {

        $parser = xml_parser_create();

        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);

        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);

        xml_parse_into_struct($parser, $xml, $tags);

        xml_parser_free($parser);


        $elements = array();  // the currently filling [child] XmlElement array

        $stack = array();

        foreach ($tags as $tag) {

          $index = count($elements);

          if ($tag['type'] == "complete" || $tag['type'] == "open") {

            $elements[$index] = new XmlElement;

            $elements[$index]->name = $tag['tag'];

            $elements[$index]->attributes = (isset($tag['attributes']))?$tag['attributes']:null;

            $elements[$index]->content = (isset($tag['value']))?$tag['value']:null;

            if ($tag['type'] == "open") {  // push

              $elements[$index]->children = array();

              $stack[count($stack)] = &$elements;

              $elements = &$elements[$index]->children;

            }

          }

          if ($tag['type'] == "close") {  // pop

            $elements = &$stack[count($stack) - 1];

            unset($stack[count($stack) - 1]);

          }

        }

        return $elements[0];  // the single top-level element

    }

    }

}



慕尼黑5688855
浏览 167回答 2
2回答

繁花不似锦

您可以使用 DOM 和 XPath 表达式。所有数据都在{https://api.nhs.uk/data/services}organisationSummary节点内。下面的示例注册service命名空间的前缀https://api.nhs.uk/data/services(这就是s示例中的 解析为)。然后它{https://api.nhs.uk/data/services}organisationSummary使用//service:organisationSummary表达式获取任何节点。对于每个组织,它$item使用标量值的表达式构建一个变量。循环用于{https://api.nhs.uk/data/services}addressLine节点。// create the DOM document and load the XML $document = new DOMDocument();$document->loadXML($xml);// create an Xpath instance for the document - register namespace$xpath = new DOMXpath($document);$xpath->registerNamespace('service', 'https://api.nhs.uk/data/services');$json = [  "success" => true,  // force object for data - it will be an array otherwise  "data" => new stdClass()];// iterate over all organisationSummary nodesforeach ($xpath->evaluate('//service:organisationSummary') as $index => $organisation) {  // fetch single values  $item = [    "name" => $xpath->evaluate('string(service:name)', $organisation),    "odscode" => $xpath->evaluate('string(service:odsCode)', $organisation),    "postcode" => $xpath->evaluate('string(service:address/service:postcode)', $organisation),    "telephone" => $xpath->evaluate('string(service:contact/service:telephone)', $organisation),    "longitude" => $xpath->evaluate('string(service:geographicCoordinates/service:longitude)', $organisation),    "latitude" => $xpath->evaluate('string(service:geographicCoordinates/service:latitude)', $organisation),    "distance" => $xpath->evaluate('string(service:Distance)', $organisation)  ];  // add the addressLine values  foreach ($xpath->evaluate('service:address/service:addressLine', $organisation) as $lineIndex => $line) {    $item['addressline'.$lineIndex] = $line->textContent;  }  // add the $item  $json['data']->{$index} = $item;}echo json_encode($json, JSON_PRETTY_PRINT);输出:{    "success": true,    "data": {        "0": {            "name": "Highfields Surgery (R Wadhwa)",            "odscode": "C82116",            "postcode": "LE2 0NN",            "telephone": "01162543253",            "longitude": "-1.11859357357025",            "latitude": "52.6293983459473",            "distance": "0.247038430918239",            "addressline0": "25 Severn Street",            "addressline1": "Leicester",            "addressline2": "Leicestershire"        },        "1": {            "name": "Dr R Kapur & Partners",            "odscode": "C82659",            "postcode": "LE2 0TA",            "telephone": "01162951258",            "longitude": "-1.11907768249512",            "latitude": "52.6310386657715",            "distance": "0.30219913005026",            "addressline0": "St Peters Health Centre",            "addressline1": "Sparkenhoe Street",            "addressline2": "Leicester",            "addressline3": "Leicestershire"        },        ...

冉冉说

您需要使用适当的方法来遍历 XML 文档,而不是查看每个元素并检查其名称。您的两个最佳选择是 DOM 方法和 XPath。如果您使用过前端 JavaScript 代码(例如document.getElementsByTagName或document.getElementById),您就会熟悉前者。后者具有更陡峭的学习曲线,但功能要强大得多。(XSLT 也是转换 XML 的好选择,但我对它不是很熟悉。)不过,第一步是使用正确的 XML 库。您使用的 XML 函数级别非常低。我建议改用 PHP 的DomDocument扩展。让我们潜入吧!<?php$xml = file_get_contents("nhs.xml");// assume XML document is stored in a variable called $xml$dom = new DomDocument;$dom->loadXml($xml);// thankfully we can ignore namespaces!$summaries = $dom->getElementsByTagName("organisationSummary");// go through each <organisationSummary> elementforeach ($summaries as $os) {&nbsp; &nbsp; $entry_data = [&nbsp; &nbsp; &nbsp; &nbsp; // note we're using $os and not $dom now, so we get child elements of this particular element&nbsp; &nbsp; &nbsp; &nbsp; "name"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=> $os->getElementsByTagName("name")[0]->textContent,&nbsp; &nbsp; &nbsp; &nbsp; "odscode"&nbsp; &nbsp; &nbsp; => $os->getElementsByTagName("odsCode")[0]->textContent,&nbsp; &nbsp; &nbsp; &nbsp; "addressline0" => $os->getElementsByTagName("addressLine")[0]->textContent,&nbsp; &nbsp; &nbsp; &nbsp; // provide a default empty string in case there's no further lines&nbsp; &nbsp; &nbsp; &nbsp; "addressline1" => $os->getElementsByTagName("addressLine")[1]->textContent ?? "",&nbsp; &nbsp; &nbsp; &nbsp; "addressline2" => $os->getElementsByTagName("addressLine")[2]->textContent ?? "",&nbsp; &nbsp; &nbsp; &nbsp; "addressline3" => $os->getElementsByTagName("addressLine")[3]->textContent ?? "",&nbsp; &nbsp; &nbsp; &nbsp; "postcode"&nbsp; &nbsp; &nbsp;=> $os->getElementsByTagName("postcode")[0]->textContent,&nbsp; &nbsp; &nbsp; &nbsp; "telephone"&nbsp; &nbsp; => $os->getElementsByTagName("telephone")[0]->textContent,&nbsp; &nbsp; &nbsp; &nbsp; "longitude"&nbsp; &nbsp; => $os->getElementsByTagName("longitude")[0]->textContent,&nbsp; &nbsp; &nbsp; &nbsp; "latitude"&nbsp; &nbsp; &nbsp;=> $os->getElementsByTagName("latitude")[0]->textContent,&nbsp; &nbsp; &nbsp; &nbsp; "distance"&nbsp; &nbsp; &nbsp;=> $os->getElementsByTagName("Distance")[0]->textContent,&nbsp; &nbsp; ];&nbsp; &nbsp; $json_data[] = $entry_data;}if (count($json_data)) {&nbsp; &nbsp; $output = ["success" => true, "data" => $json_data];} else {&nbsp; &nbsp; $output = ["success" => false];}echo json_encode($output, JSON_PRETTY_PRINT);
随时随地看视频慕课网APP
我要回答