Как я могу разобрать XML-данные в массив с помощью php?

У меня нет опыта работы с XML, и я пытался понять это без особого прогресса.

Мне нужно получить результаты из файла XML в массив с помощью PHP.

Вот XML

<ns2:messageContainer xmlns = "datex2.eu/schema/3/common" xmlns:ns2 = "datex2.eu/schema/3/messageContainer" xmlns:ns3 = "datex2.eu/schema/3/exchangeInformation" xmlns:ns4 = "datex2.eu/schema/3/informationManagement" xmlns:ns5 = "datex2.eu/schema/3/dataDictionaryExtension" xmlns:ns6 = "datex2.eu/schema/3/cctvExtension" xmlns:ns7 = "datex2.eu/schema/3/locationReferencing" xmlns:ns8 = "datex2.eu/schema/3/alertCLocationCodeTableExtension" xmlns:ns9 = "datex2.eu/schema/3/extension" xmlns:ns10 = "datex2.eu/schema/3/roadTrafficData" xmlns:ns11 = "datex2.eu/schema/3/vms" xmlns:ns12 = "datex2.eu/schema/3/situation" modelBaseVersion = "3">
    <ns2:payload xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:type = "ns7:PredefinedLocationsPublication" lang = "no" modelBaseVersion = "3">
        <publicationTime>2023-01-25T13:56:15.615+01:00</publicationTime>
        <publicationCreator>
            <country>no</country>
            <nationalIdentifier>NPRA</nationalIdentifier>
        </publicationCreator>
        <ns7:headerInformation>
            <confidentiality>noRestriction</confidentiality>
            <informationStatus>real</informationStatus>
        </ns7:headerInformation>

        <ns7:predefinedLocationReference xsi:type = "ns7:PredefinedLocation" id = "100356" version = "1">
            <ns7:predefinedLocationName>
                <values>
                    <value lang = "no">Eikås - Åsanevegen</value>
                </values>
            </ns7:predefinedLocationName>
            <ns7:location xsi:type = "ns7:LinearLocation">
                <ns7:gmlLineString srsName = "http://www.opengis.net/gml/srs/epsg.xml#32633">
                    <ns7:posList>-27946 6743813</ns7:posList>
                </ns7:gmlLineString>
            </ns7:location>
        </ns7:predefinedLocationReference>
        <ns7:predefinedLocationReference xsi:type = "ns7:PredefinedLocation" id = "100361" version = "1">
            <ns7:predefinedLocationName>
                <values>
                    <value lang = "no">Ammerud - Bjerke</value>
                </values>
            </ns7:predefinedLocationName>
            <ns7:location xsi:type = "ns7:LinearLocation">
                <ns7:gmlLineString srsName = "http://www.opengis.net/gml/srs/epsg.xml#32633">
                    <ns7:posList>269553 6653843</ns7:posList>
                </ns7:gmlLineString>
            </ns7:location>
        </ns7:predefinedLocationReference>
    </ns2:payload>
    <ns2:exchangeInformation modelBaseVersion = "3">
        <ns3:exchangeContext>
            <ns3:codedExchangeProtocol>snapshotPull</ns3:codedExchangeProtocol>
            <ns3:exchangeSpecificationVersion>3</ns3:exchangeSpecificationVersion>
            <ns3:supplierOrCisRequester>
                <ns3:internationalIdentifier>
                    <country>no</country>
                    <nationalIdentifier>NPRA</nationalIdentifier>
                </ns3:internationalIdentifier>
            </ns3:supplierOrCisRequester>
        </ns3:exchangeContext>
        <ns3:dynamicInformation>
            <ns3:exchangeStatus>undefined</ns3:exchangeStatus>
            <ns3:messageGenerationTimestamp>2023-01-25T13:56:15.615+01:00</ns3:messageGenerationTimestamp>
        </ns3:dynamicInformation>
    </ns2:exchangeInformation>
</ns2:messageContainer>

Вот мой PHP-код

        $xml = simplexml_load_string($response->raw_body, "SimpleXMLElement", LIBXML_NOCDATA, 'ns2', true);
        
        $xml->registerXPathNamespace('ns7','http://datex2.eu/schema/3/locationReferencing');

        $count = 0;
        foreach($xml->xpath('//ns7:predefinedLocationReference') as $event) {

            $return[$count]['id'] = intval($event->attributes()->id);
            $predefinedLocationName = $event->xpath('ns7:predefinedLocationName');
            foreach ($predefinedLocationName[0]->values as $locVal) {
                $return[$count]['name'] = strval($locVal->value);
            }

            $count++;
        }

Я уверен, что есть лучший способ, но вот что я получил:

            {
                "id": 100356,
                "name": "Eikås - Åsanevegen"
            },
            {
                "id": 100361,
                "name": "Ammerud - Bjerke"
            }

Чего мне не хватает, так это получить значение posList из XML и добавить его в мой массив в PHP.

Что именно затрудняет получение этого элемента? Конечно, есть и другие способы написания всего кода, но вы должны быть в состоянии получить posList почти так же, как вы получаете имя.

IMSoP 25.01.2023 23:25

Между прочим, вам не нужно использовать чужие бессмысленные префиксы, такие как «ns7», для обозначения пространств имен — и, вероятно, не следует, потому что они выглядят автоматически сгенерированными и, вероятно, изменятся в будущем, оставив вас в замешательстве, где ваши «ns7» теперь является «ns6» документа. Дайте им псевдоним, который имеет смысл для вас, например $xml->registerXPathNamespace('locRef','http://datex2.eu/sche‌​ma/3/locationReferen‌​cing');

IMSoP 25.01.2023 23:28

Спасибо за ваш ответ @IMSoP. Что бы я ни пробовал, я получаю пустой posList.

JIMJA 25.01.2023 23:45

Ваша основная проблема в том, что value и postList находятся в разных пространствах имен, и вам нужно объявить их обоих. Кроме того, вместо этого я бы использовал DOMDocument из-за (среди прочего) его лучшей поддержки xpath и пространства имен.

Jack Fleeting 26.01.2023 02:09

@JackFleeting Очевидно, что вкусы различаются, но я нахожу DOM чрезвычайно многословным и неудобным, и в первую очередь не стал бы беспокоиться о XPath для этой проблемы. В SimpleXML есть некоторые части, которые поначалу не очевидны, но как только вы их поймете, в основном он будет соответствовать своему названию.

IMSoP 26.01.2023 10:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
5
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вместо того, чтобы возиться с XPath, я бы использовал основные методы доступа SimpleXML, отметив эту ссылку о том, как SimpleXML обрабатывает пространства имен.

В частности, я бы отметил путь, который я хотел пройти по документу, расширяя пространства имен до их полных идентификаторов, а не до их локальных псевдонимов:

  • Начните с messageContainer, в пространстве имен datex2.eu/schema/3/messageContainer
  • Войдите в payload, в том же пространстве имен
  • Перебрать каждый predefinedLocationReference в пространстве имен datex2.eu/schema/3/locationReferencing
  • Получите «id» из атрибута (без пространства имен) id
  • Войдите в predefinedLocationName, все еще в пространстве имен datex2.eu/schema/3/locationReferencing
  • Перейдите в values, в пространство имен datex2.eu/schema/3/common (определено как xmlns по умолчанию в верхней части документа)
  • Получите «имя» из элемента value в этом пространстве имен.
  • Из predefinedLocationReference, которое у нас было ранее, перейдите в location (в том же пространстве имен, что и predefinedLocationReference)
  • Войдите в gmlLineString, в том же пространстве имен
  • Получите «postList» из posList в том же пространстве имен.

Затем это переводится непосредственно в этот PHP-код:

// Some constants to make namespaces easier to read
const NS_MSG_CONT = 'datex2.eu/schema/3/messageContainer';
const NS_LOC_REF = 'datex2.eu/schema/3/locationReferencing';
const NS_COMMON = 'datex2.eu/schema/3/common';

// Initialise our return array
$return = [];

// Start at `messageContainer`, in the `datex2.eu/schema/3/messageContainer` namespace
$xml = simplexml_load_string($response->raw_body, "SimpleXMLElement", 0, NS_MSG_CONT);

// Go into `payload`, in the same namespace
$payload = $xml->payload;

// Loop over each `predefinedLocationReference`, in the `datex2.eu/schema/3/locationReferencing` namespace
foreach ($payload->children(NS_LOC_REF)->predefinedLocationReference as $predefinedLocationReference ) {
    // Initialise the return item
    $item = [];

    // Get the "id" from the (non-namespaced) `id` attribute
    $item['id'] = (string)$predefinedLocationReference->attributes(null)->id;

    // Go into `predefinedLocationName`, still in the `datex2.eu/schema/3/locationReferencing` namespace
    $predefinedLocationName = $predefinedLocationReference->predefinedLocationName;

    // Go into `values`, in the `datex2.eu/schema/3/common` namespace (defined as the default `xmlns` at the top of the document)
    $values = $predefinedLocationName->children(NS_COMMON)->values;

    // Get the "name" from the `value` element in that namespace
    $item['name'] = (string)$values->value;

    // From the `predefinedLocationReference` we had earlier, go into the `location` (in the same namespace as `predefinedLocationReference`)
    $location = $predefinedLocationReference->location;

    // Go into `gmlLineString`, in the same namespace
    $gmlLineString = $location->gmlLineString;

    // Get the "posList" from the `posList`, in the same namespace
    $item['posList'] = (string)$gmlLineString->posList;

    // Add item to our final results
    $return[] = $item;
}

// Test
var_dump($return);

Это, очевидно, можно сделать намного короче, удалив комментарии и промежуточные переменные по вкусу; очень сокращенная версия точно такого же кода выглядит так:

const NS_MSG_CONT = 'datex2.eu/schema/3/messageContainer';
const NS_LOC_REF = 'datex2.eu/schema/3/locationReferencing';
const NS_COMMON = 'datex2.eu/schema/3/common';

$xml = simplexml_load_string($raw_body, "SimpleXMLElement", 0, NS_MSG_CONT);
// Note: in PHP >8.0, you can skip the parameters you're not interested in:
// $xml = simplexml_load_string($raw_body, namespace_or_prefix: NS_MSG_CONT);
$return = [];
foreach ($xml->payload->children(NS_LOC_REF)->predefinedLocationReference as $predefinedLocationReference ) {
    $return[] = [
        'id' => (string)$predefinedLocationReference->attributes(null)->id,
        'name' => (string)$predefinedLocationReference->predefinedLocationName->children(NS_COMMON)->values->value,
        'posList' => (string)$predefinedLocationReference->location->gmlLineString->posList,
    ];
}

Большое спасибо @IMSoP за это. Работает именно так, как я хотел!

JIMJA 26.01.2023 11:38

Как вы говорите в своем комментарии выше, вкусы действительно различаются; но в то время как я все еще твердо в лагере xpath/domdoc, я должен сказать, что Ваш краткий ответ формы - вещь красоты!

Jack Fleeting 26.01.2023 12:30

Другие вопросы по теме