goetas-webservices / xsd2php

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

xml attribute cause Uncaught ReflectionException: Class \ does not exist because it seaches empty class #129

Closed ricoos closed 3 years ago

ricoos commented 3 years ago

Hello

I wand to deserialize an xml that has attributes which is xs:NCName in xsd. In metadata file, it is <xs:attribute name="issuing-type" use="required"/> . For example (...)<invoice issuing-type="DEMAT">(...) if I remove the attributes in my xml it works. If I leave the attributes it throws Uncaught ReflectionException: Class \ does not exist in C:\xampp\htdocs\test\import12\vendor\jms\metadata\src\MetadataFactory.php:175

Can you help me understand where is my mistake ?

During deserialization of an xml with attributes, the classloader is called with an empty class name, the stack trace shows "[0]" object on line number 5. Here is a trace if classloader in case serached class is empty

 #0  Composer\Autoload\ClassLoader->loadClass()
#1  spl_autoload_call()
#2  ReflectionClass->__construct(\) called at [C:\xampp\htdocs\test\import12\vendor\jms\metadata\src\MetadataFactory.php:175]
#3  Metadata\MetadataFactory->getClassHierarchy(\) called at [C:\xampp\htdocs\test\import12\vendor\jms\metadata\src\MetadataFactory.php:75]
#4  Metadata\MetadataFactory->getMetadataForClass(\) called at [C:\xampp\htdocs\test\import12\vendor\jms\serializer\src\GraphNavigator\DeserializationGraphNavigator.php:171]
#5  JMS\Serializer\GraphNavigator\DeserializationGraphNavigator->accept(SimpleXMLElement **Object ([0] => DEMAT)**, Array ([name] => \,[params] => Array ())) called at [C:\xampp\htdocs\test\import12\vendor\jms\serializer\src\XmlDeserializationVisitor.php:312]
#6  JMS\Serializer\XmlDeserializationVisitor->visitProperty(JMS\Serializer\Metadata\PropertyMetadata Object ([sinceVersion] => ,[untilVersion] => ,[groups] => ,[serializedName] => **issuing-type**,[type] => Array ([name] => \,[params] => Array ()),  (...) ,[getter] => getIssuingType,[setter] => setIssuingType,[inline] => ,[skipWhenEmpty] => ,[readOnly] => ,[xmlAttributeMap] => ,[maxDepth] => ,[excludeIf] => ,[forceReflectionAccess] => ,[class] => App\Bindings\Alkor\Invoices\Invoice,**[name] => issuingType**) , SimpleXMLElement Object **([@attributes] => Array ([issuing-type] => DEMAT)**,[placeOfSupply] => FR,[exclusive-tax-amount] => 32.40,[charged] => SimpleXMLElement Object ([external-ref] => 991004003615))) called at [C:\xampp\htdocs\test\import12\vendor\jms\serializer\src\GraphNavigator\DeserializationGraphNavigator.php:215]
#7  JMS\Serializer\GraphNavigator\DeserializationGraphNavigator->accept(SimpleXMLElement Object ([@attributes] => Array ([issuing-type] => DEMAT),[placeOfSupply] => FR,[exclusive-tax-amount] => 32.40,............) called at [C:\xampp\htdocs\test\import12\vendor\jms\serializer\src\XmlDeserializationVisitor.php:222]
#8  JMS\Serializer\XmlDeserializationVisitor->visitArray(SimpleXMLElement Object ([invoice] => SimpleXMLElement Object ([@attributes] => Array ([issuing-type] => DEMAT),[placeOfSupply] => FR,[exclusive-tax-amount] => 32.40,[charged] => SimpleXMLElement Object ([external-ref] => 991004003615))), Array ([name] => array,[params] => Array ([0] => Array ([name] => App\Bindings\XXX\Invoices\Invoice,[params] => Array ())))) called at [C:\xampp\htdocs\test\import12\vendor\jms\serializer\src\GraphNavigator\DeserializationGraphNavigator.php:141]
#9  JMS\Serializer\GraphNavigator\DeserializationGraphNavigator->accept(SimpleXMLElement Object ([invoice] => SimpleXMLElement Object ([@attributes] => Array ([issuing-type] => DEMAT),[placeOfSupply] => FR,[exclusive-tax-amount] => 32.40,[charged] => SimpleXMLElement Object ([external-ref] => 991004003615))), Array ([name] => array,[params] => Array ([0] => Array ([name] => App\Bindings\XXX\Invoices\Invoice,[params] => Array ())))) called at [C:\xampp\htdocs\test\import12\vendor\jms\serializer\src\XmlDeserializationVisitor.php:337]
#10 JMS\Serializer\XmlDeserializationVisitor->visitProperty(JMS\Serializer\Metadata\PropertyMetadata Object ([sinceVersion] => ......
#11 JMS\Serializer\GraphNavigator\DeserializationGraphNavigator->accept(SimpleXMLElement Object ([invoice] => .....
#12 JMS\Serializer\Serializer->visit(JMS\Serializer\GraphNavigator\DeserializationGraphNavigator Object (......
#13 JMS\Serializer\Serializer->deserialize(
    FR
    32.40 
        991004003615 

During deserialization of an xml without attributes, the classloader is called , the stack trace shows showing "invoice" on line number 5

0 Composer\Autoload\ClassLoader->loadClass(App\Bindings\XXX\Invoices\Invoices)

1 spl_autoload_call(App\Bindings\XXX\Invoices\Invoices)

(...)

5 JMS\Serializer\GraphNavigator\DeserializationGraphNavigator->accept(SimpleXMLElement Object ([invoice] => SimpleXMLElement Object ([placeOfSupply] => FR,[exclusive-tax-amount] =(...)

here is App.Bindings.XXX.Invoices.Invoice.yml :

App\Bindings\XXX\Invoices\Invoice:
    xml_root_name: invoice
    properties:
        invoiceType:
            expose: true
            access_type: public_method
            serialized_name: invoice-type
            accessor:
                getter: getInvoiceType
                setter: setInvoiceType
            xml_attribute: true
            type: string
        issuingType:
            expose: true
            access_type: public_method
            serialized_name: issuing-type
            accessor:
                getter: getIssuingType
                setter: setIssuingType
            xml_attribute: true
            type: \AnyType 
        charged:
            expose: true
            access_type: public_method
            serialized_name: charged
            accessor:
                getter: getCharged
                setter: setCharged
            type: App\Bindings\XXX\Invoices\Charged
        placeOfSupply:
            expose: true
            access_type: public_method
            serialized_name: placeOfSupply
            accessor:
                getter: getPlaceOfSupply
                setter: setPlaceOfSupply
            type: string 
        exclusiveTaxAmount:
            expose: true
            access_type: public_method
            serialized_name: exclusive-tax-amount
            accessor:
                getter: getExclusiveTaxAmount
                setter: setExclusiveTaxAmount
            type: float

here is my php class file Invoice.php :

class Invoice{
    private $invoiceType = null;
    private $issuingType = null;
    public function getIssuingType()  {
        return $this->issuingType;
    }
    public function setIssuingType($issuingType)   {
        $this->issuingType = $issuingType;
        return $this;
    }
   (...)
}

my xml file that fails

<?xml version="1.0" encoding="utf-8"?>
<invoices>
    <invoice issuing-type="DEMAT"  >    
        <placeOfSupply>FR</placeOfSupply>
        <exclusive-tax-amount>32.40</exclusive-tax-amount>
        <charged>
            <external-ref>991004003615</external-ref>
        </charged>
    </invoice>
</invoices>

my xml file that works

<?xml version="1.0" encoding="utf-8"?>
<invoices>
    <invoice    >   
        <placeOfSupply>FR</placeOfSupply>
        <exclusive-tax-amount>32.40</exclusive-tax-amount>
        <charged>
            <external-ref>991004003615</external-ref>
        </charged>
    </invoice>
</invoices>

my calling php code :

require  'vendor/autoload.php'; 
use App\Bindings\XXX\Invoices\Invoices;
use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\Handler\HandlerRegistryInterface;
use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\BaseTypesHandler;
use GoetasWebservices\Xsd\XsdToPhpRuntime\Jms\Handler\XmlSchemaDateHandler; 

$serializerBuilder  = JMS\Serializer\SerializerBuilder::create()  
    ->addMetadataDir('src/Bindings/XXX/InvoicesMetadataFullFQDN');    

 $serializerBuilder->configureHandlers(function (HandlerRegistryInterface $handler) use ($serializerBuilder) {
        $serializerBuilder->addDefaultHandlers();
        $handler->registerSubscribingHandler(new BaseTypesHandler()); // XMLSchema List handling
        $handler->registerSubscribingHandler(new XmlSchemaDateHandler()); // XMLSchema date handling 
});
$serializer = $serializerBuilder ->build();
$exampleData = file_get_contents('invoice_ADH991_simple2.xml');
$exampleClass = 'App\Bindings\XXX\Invoices\Invoices';
$object = $serializer->deserialize($exampleData, $exampleClass, 'xml'); 
var_dump($object);

extract from xsd that originate php class

  <xs:element name="invoice">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="issuer"/>
        (...)
      </xs:sequence>    
    <xs:attribute name="issuing-type" use="required"/> 
      <xs:attribute name="number" use="required" type="xs:NCName"/>
    </xs:complexType>
  </xs:element>
goetas commented 3 years ago

I see \AnyType. Did you check the section on how to handle the xsd any type (https://github.com/goetas-webservices/xsd2php#dealing-with-xsdanytype-or-xsdanysimpletype)?

The error is on the attribute issuing-type that is marked as "any type"

ricoos commented 3 years ago

Thanks for you help. In fact AnyType was because of a poorly defined XSD in the first place so I went the other way regarding \AnyType; I made a correction in the XSD (added a type = string for element issuing-type) and have no \AnyType anymore. It works well now with attribute issuing-type. I will try to avoid AnyType where possible, as I am too much a beginner to deal with XmlSerializationVisitor and stuff.