xsd2php icon indicating copy to clipboard operation
xsd2php copied to clipboard

simpleType union not converted correctly

Open tpraxl opened this issue 5 years ago • 10 comments

I'm trying to use xsd2php for OTA 2003/05 / 2014A-1.0. In addition to #71, there's also a problem with the following definition in OTA_SimpleTypes.xsd

<xs:simpleType name="DateOrTimeOrDateTimeType">
  <xs:annotation>
    <xs:documentation xml:lang="en">A construct to validate either a date or a time or a dateTime value.</xs:documentation>
  </xs:annotation>
  <xs:union memberTypes="xs:date xs:dateTime xs:time"/>
</xs:simpleType>

The following valid xml causes an error

<TimeSpan Start="2018-11-12T12:00:00" End="2018-11-13T12:00:00"></TimeSpan>
RuntimeException: Invalid date "2018-11-12T12:00:00", expected valid XML Schema date string.

This is the generated DateTimeSpanType.yml

Hsp\latest\DateTimeSpanType:
    properties:
        start:
            expose: true
            access_type: public_method
            serialized_name: Start
            accessor:
                getter: getStart
                setter: setStart
            xml_attribute: true
            type: GoetasWebservices\Xsd\XsdToPhp\XMLSchema\Date
…

Looks as if the yml doesn't contain the other valid types. I guess this is not possible? Does anyone know a workaround? This is real life XML. I cannot influence the XML and when I tried to modify the xsd to use xs:dateTime only, it choked on another real life XML that has no time part. I need to handle both cases.

tpraxl avatar Nov 06 '18 15:11 tpraxl

is XmlSchemaDateHandler registered as in https://github.com/goetas-webservices/xsd2php#serialize--unserialize ?

goetas avatar Nov 06 '18 15:11 goetas

Yes:

…
use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\BaseTypesHandler;
use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\XmlSchemaDateHandler;

class Serializer
{
    /**
     * @var JMSSerializer
     */
    private $serializer;

    public function build($version = 'latest'): Serializer
    {
        $serializerBuilder = SerializerBuilder::create();
        $serializerBuilder->addMetadataDir(
            dirname(__FILE__) . "/../generated/${version}/meta",
            "Hsp\\${version}"
        );
        $serializerBuilder->configureHandlers(function (HandlerRegistryInterface $handler) use ($serializerBuilder) {
            $serializerBuilder->addDefaultHandlers();
            $handler->registerSubscribingHandler(new BaseTypesHandler()); // XMLSchema List handling
            $handler->registerSubscribingHandler(new XmlSchemaDateHandler()); // XMLSchema date handling
        });
        $this->serializer = $serializerBuilder->build();
        return $this;
    }

    public function serialize($model): string
    {
        return $this->serializer->serialize($model, 'xml');
    }

    public function deserialize($xmlString, $className)
    {
        return $this->serializer->deserialize($xmlString, $className, 'xml');
    }
}

tpraxl avatar Nov 06 '18 16:11 tpraxl

~the bug looks related to https://github.com/goetas-webservices/xsd2php-runtime/blob/18a9e25e89b719fa5417dcbd6515604723df957d/src/Jms/Handler/XmlSchemaDateHandler.php#L92~

goetas avatar Nov 06 '18 16:11 goetas

The union type should be detected as GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime instead of GoetasWebservices\Xsd\XsdToPhp\XMLSchema\Date.

To solve it you can add a type alias (see alias in the config file)

goetas avatar Nov 06 '18 16:11 goetas

Thanks. How am I supposed to configure that? Tried

xsd2php:
…
  aliases:
    'http://www.opentravel.org/OTA/2003/05/common':
      DateOrDateTimeType: 'GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime'

But that doesn't work. It still resolves to

type: GoetasWebservices\Xsd\XsdToPhp\XMLSchema\Date

tpraxl avatar Nov 06 '18 16:11 tpraxl

Ok. Found the correct alias configuration:

  aliases:
    'http://www.opentravel.org/OTA/2003/05':
      DateOrTimeOrDateTimeType: 'GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime'

But now, it chokes when I try to set the date in another test:

   $statusApplicationControl1 = new StatusApplicationControlType();
   $statusApplicationControl1->setStart(Carbon::parse('2018-11-06')->toDate());

Notice that Carbon::toDate returns a DateTime. This part of the test worked before I configured the alias.

TypeError: Argument 1 passed to Hsp\latest\StatusApplicationControlType::setStart() must be an instance of GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime, instance of DateTime given, called in […]/tests/SerializerTest.php on line 214

I would use GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime instead of DateTime if I needed to, but I'm not sure how to do that, because I can't find that type at all.

Can you tell me, how to convert a DateTime to GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime?

This is the generated code for StatusApplicationControlType.php:

 /**
     * Sets a new start
     *
     * The starting value of the time span.
     *
     * @param \GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime $start
     * @return self
     */
    public function setStart(\GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime $start)
    {
        $this->start = $start;
        return $this;
    }

tpraxl avatar Nov 07 '18 12:11 tpraxl

So I tested the following:

I removed the alias config that was meant to solve the first problem but caused the wrong type hint in the generated StatusApplicationControlType and other files.

#  aliases:
#    'http://www.opentravel.org/OTA/2003/05':
#      DateOrTimeOrDateTimeType: 'GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime'

Then I manually changed the generated DateTimeSpanType.yml:

Hsp\latest\DateTimeSpanType:
    properties:
        start:
            expose: true
            access_type: public_method
            serialized_name: Start
            accessor:
                getter: getStart
                setter: setStart
            xml_attribute: true
# from  type: GoetasWebservices\Xsd\XsdToPhp\XMLSchema\Date
# to
            type: GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime
# same with property "end"

Everything works as expected. But after each conversion, you need to manually adjust the generated yml.

Is there a way to avoid this manual correction? Can I generate the yml with GoetasWebservices\Xsd\XsdToPhp\XMLSchema\DateTime and keep the \DateTime typehint in the generated php classes (which finally works as expected)?

   /**
     * Sets a new start
     *
     * The starting value of the time span.
     *
     * @param \DateTime $start
     * @return self
     */
    public function setStart(\DateTime $start)
    {
        $this->start = $start;
        return $this;
    }

tpraxl avatar Nov 08 '18 07:11 tpraxl

having a similar problem, without specifying an alias, typehints generated are \Datetime, when I add an alias, the typehints are GoetasWebservices\Xsd\XsdToPhp\XMLSchema\Date, but this class does not seem to exist, at least I could not find it in this package nor the runtime one. What is the correct way of handling date types? are there any docs about this?

cebe avatar Apr 25 '19 11:04 cebe

@cebe Don't forget to register these custom types.... ie. BaseTypes/XmlSchemaDate

  $serializerBuilder->configureHandlers(
            static function (HandlerRegistryInterface $handler) use ($serializerBuilder) {
                $serializerBuilder->addDefaultHandlers();
                $handler->registerSubscribingHandler(new BaseTypesHandler()); // XMLSchema List handling
                $handler->registerSubscribingHandler(new XmlSchemaDateHandler()); // XMLSchema date handling
                // $handler->registerSubscribingHandler(new YourhandlerHere());
            }
        );

Those files come from the namespace GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler;

Kalyse avatar Apr 25 '19 11:04 Kalyse

I think I got it now, thanks for the hint!

cebe avatar Apr 25 '19 14:04 cebe