goetas-webservices / xsd2php

Convert XSD into PHP classes and JMS serializer definitions
MIT License
238 stars 92 forks source link

xml_list deserialization does not work #18

Open robinlehrmann opened 7 years ago

robinlehrmann commented 7 years ago

Hi,

This yml metadata was generated:

Mobile\Search\SearchResult:
    xml_root_name: search-result
    xml_root_namespace: 'http://services.mobile.de/schema/search'
    properties:
        total:
            expose: true
            access_type: public_method
            serialized_name: total
            xml_element:
                namespace: 'http://services.mobile.de/schema/search'
            accessor:
                getter: getTotal
                setter: setTotal
            type: integer
        pageSize:
            expose: true
            access_type: public_method
            serialized_name: page-size
            xml_element:
                namespace: 'http://services.mobile.de/schema/search'
            accessor:
                getter: getPageSize
                setter: setPageSize
            type: integer
        currentPage:
            expose: true
            access_type: public_method
            serialized_name: current-page
            xml_element:
                namespace: 'http://services.mobile.de/schema/search'
            accessor:
                getter: getCurrentPage
                setter: setCurrentPage
            type: integer
        maxPages:
            expose: true
            access_type: public_method
            serialized_name: max-pages
            xml_element:
                namespace: 'http://services.mobile.de/schema/search'
            accessor:
                getter: getMaxPages
                setter: setMaxPages
            type: integer
        errors:
            expose: true
            access_type: public_method
            serialized_name: errors
            xml_element:
                namespace: 'http://services.mobile.de/schema/common/error-1.0'
            accessor:
                getter: getErrors
                setter: setErrors
            type: array<Mobile\Common\Error\Error>
            xml_list:
                inline: false
                entry_name: error
                namespace: 'http://services.mobile.de/schema/search'
        ads:
            expose: true
            access_type: public_method
            serialized_name: ads
            xml_element:
                namespace: 'http://services.mobile.de/schema/search'
            accessor:
                getter: getAds
                setter: setAds
            type: array<Mobile\Ad\Ad>
            xml_list:
                inline: false
                entry_name: ad
                namespace: 'http://services.mobile.de/schema/search'

I've created the serializer with required handlers:

<?php declare(strict_types = 1);

namespace Mobile\Serializer;

use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\BaseTypesHandler;
use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\XmlSchemaDateHandler;
use JMS\Serializer\Handler\HandlerRegistry;
use JMS\Serializer\Serializer;
use JMS\Serializer\SerializerBuilder;

class SerializerFactory
{
    private $metaDataDirectory;
    private $prefix;

    public function __construct(string $metaDataDirectory, string $prefix)
    {
        $this->metaDataDirectory = $metaDataDirectory;
        $this->prefix = $prefix;
    }

    public function create(): Serializer
    {
        $serializerBuilder = SerializerBuilder::create();
        return $serializerBuilder
            ->configureHandlers(function (HandlerRegistry $handlerRegistry) use ($serializerBuilder) {
                $serializerBuilder->addDefaultHandlers();
                $handlerRegistry->registerSubscribingHandler(new BaseTypesHandler());
                $handlerRegistry->registerSubscribingHandler(new XmlSchemaDateHandler());
            })
            ->addMetadataDir(
                __DIR__.'/../Resources/'.$this->metaDataDirectory, $this->prefix
            )
            ->build()
        ;
    }
}

xsdconfig:

xsd2php:
  namespaces:
    'http://services.mobile.de/schema/search': 'Mobile\Search'
    'http://services.mobile.de/schema/ad': 'Mobile\Ad'
    'http://services.mobile.de/schema/resource': 'Mobile\Resource'
    'http://services.mobile.de/schema/seller': 'Mobile\Seller'
    'http://services.mobile.de/schema/common/financing-1.0': 'Mobile\Common\Financing'
    'http://services.mobile.de/schema/common/error-1.0': 'Mobile\Common\Error'
  destinations_php:
    'Mobile\Search': Search
    'Mobile\Ad': Ad
    'Mobile\Resource': Resource
    'Mobile\Seller': Seller
    'Mobile\Common\Financing': Common\Financing
    'Mobile\Common\Error': Common\Error
  destinations_jms:
    'Mobile': Resources/serializer
  aliases:
    'http://services.mobile.de/schema/ad':
      class: 'Mobile\Ad\Clazz'
    'http://services.mobile.de/schema/seller':
      value: 'string'
  naming_strategy: short
  path_generator: psr4

If I try to deserialize this response:

I want to deserialize following xml response:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<search:search-result xmlns:seller="http://services.mobile.de/schema/seller"
                      xmlns:ad="http://services.mobile.de/schema/ad"
                      xmlns:search="http://services.mobile.de/schema/search"
                      xmlns:financing="http://services.mobile.de/schema/common/financing-1.0"
                      xmlns:resource="http://services.mobile.de/schema/resource"
                      xmlns:error="http://services.mobile.de/schema/common/error-1.0">
    <search:total>14825</search:total>
    <search:page-size>20</search:page-size>
    <search:current-page>1</search:current-page>
    <search:max-pages>742</search:max-pages>
    <search:ads>
        <ad:ad key="3" url="https://services.mobile.de/search-api/ad/3">
            <ad:creation-date value="2015-12-21T15:47:26+01:00"/>
            <ad:modification-date value="2017-01-13T16:15:10+01:00"/>
            <ad:detail-page url="http://suchen.mobile.de/auto-inserat/mercedes-benz-200-hamburg/3.html"/>
            <!-- [...] -->
        </ad:ad>
<!-- [...] -->

My composer.json:

    "require": {
        "php": "~7.0",
        "symfony/options-resolver": "~2.3|~3.0",
        "goetas-webservices/xsd2php-runtime":"^0.2.2",
        "jms/serializer": "1.5.0-RC1",
        "php-http/guzzle6-adapter": "^1.1",
        "php-http/mock-client": "^0.3"
    },
    "require-dev": {
        "phpunit/phpunit": "^5.7",
        "goetas-webservices/xsd2php":"^0.2"
    },

latests versions are installed.

The property "ads" of "SearchResult" seems to be empty. Do you have any ideas to solve my problem ?

you can clone my repository from here https://github.com/robinlehrmann/mobile-de-api to debug it

Thank you very much!

ghost commented 7 years ago

I have developed a bundle (non public) for the same purpose (mobile.de API) and encountered the same problem. You just need to correct the ad namespace:


    xml_root_name: search-result
    xml_root_namespace: 'http://services.mobile.de/schema/search'
    properties:
        [...]
        ads:
            expose: true
            access_type: public_method
            serialized_name: ads
            xml_element:
                namespace: 'http://services.mobile.de/schema/search'
            accessor:
                getter: getAds
                setter: setAds
            type: array<Mobile\Ad\Ad>
            xml_list:
                inline: false
                entry_name: ad
                namespace: 'http://services.mobile.de/schema/ad' <---- here
robinlehrmann commented 7 years ago

@ama-gi you save my day! It's working now. Thank you very much. Do you think there is a bug in this library or do you think it came from mobile.de ?

ghost commented 7 years ago

The xsd files from mobile.de look fine to me, I think its a bug. The errors xml list probably has the same problem, i think it should be:

errors:
    [...]
    xml_list:
        inline: false
        entry_name: error
        namespace: 'http://services.mobile.de/schema/common/error-1.0'

instead of

errors:
    [...]
    xml_list:
        inline: false
        entry_name: error
        namespace: 'http://services.mobile.de/schema/search'

I guess maybe this library ignores the ref attribute for the child elements (ref="ad:ad")?


<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://services.mobile.de/schema/search" xmlns:search="http://services.mobile.de/schema/search" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ad="http://services.mobile.de/schema/ad" xmlns:error="http://services.mobile.de/schema/common/error-1.0" elementFormDefault="qualified">

    <xs:import namespace="http://services.mobile.de/schema/ad" schemaLocation="ad-1.0.xsd"/>
    <xs:import namespace="http://services.mobile.de/schema/common/error-1.0" schemaLocation="common/error-1.0.xsd"/>

    <xs:element name="result">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="ad:ad" minOccurs="0" maxOccurs="unbounded"/>
            </xs:sequence>
            <xs:attribute name="total" use="required" type="xs:int"/>
            <xs:attribute name="page-size" use="required" type="xs:int"/>
            <xs:attribute name="current-page" use="required" type="xs:int"/>
            <xs:attribute name="max-pages" use="required" type="xs:int"/>
        </xs:complexType>
    </xs:element>

    <xs:element name="search-result">
        <xs:annotation>
            <xs:documentation>The ad-search-result including some metadata, probably warnings and the ads (for this
                page).
            </xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element name="total" type="xs:int">
                    <xs:annotation>
                        <xs:documentation>The total number of results.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name="page-size" type="xs:int">
                    <xs:annotation>
                        <xs:documentation>The number of results per page.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name="current-page" type="xs:int">
                    <xs:annotation>
                        <xs:documentation>The current page number.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name="max-pages" type="xs:int">
                    <xs:annotation>
                        <xs:documentation>The total number of results-pages.</xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element ref="error:errors" minOccurs="0">    <------ HERE
                    <xs:annotation>
                        <xs:documentation>A list of non fatal errors related to this search request e.g. invalid or
                            deprecated search parameters. If this list is present then the search result might not be as
                            intended by the client, so always check that this not present.
                        </xs:documentation>
                    </xs:annotation>
                </xs:element>
                <xs:element name="ads">
                    <xs:annotation>
                        <xs:documentation>The ad results (of this page).</xs:documentation>
                    </xs:annotation>
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element ref="ad:ad" minOccurs="0" maxOccurs="unbounded"/>   <------ HERE
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:simpleType name="event-type">
        <xs:restriction base="xs:string">
            <xs:enumeration value="AD_CREATE_OR_UPDATE">
                <xs:annotation>
                    <xs:documentation/>
                </xs:annotation>
            </xs:enumeration>
            <xs:enumeration value="AD_DELETE">
                <xs:annotation>
                    <xs:documentation/>
                </xs:annotation>
            </xs:enumeration>
            <xs:enumeration value="ERROR">
                <xs:annotation>
                    <xs:documentation/>
                </xs:annotation>
            </xs:enumeration>
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="event">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="event-type" type="search:event-type" minOccurs="0"/>
                <xs:element name="ad-key" type="xs:token" minOccurs="0"/>
                <xs:element ref="ad:ad" minOccurs="0"/>
                <xs:element ref="error:errors" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>`
robinlehrmann commented 7 years ago

Okay, we should try to fix that via a pull-request. I will look for it as soon as posible.

goetas commented 7 years ago

@robinlehrmann any news?

robinlehrmann commented 7 years ago

@goetas No, I'm sorry, currently I haven't enough time to resolve it.

duhecx commented 6 years ago

Anyone had a work around or any solid solution for this?