Skip to content

Conversation

@evert
Copy link
Member

@evert evert commented Dec 2, 2015

cc: @staabm. What do you think?

@staabm
Copy link
Member

staabm commented Dec 2, 2015

Any chanche we can get the classmap beeing used for reading and writing?

@evert
Copy link
Member Author

evert commented Dec 2, 2015

That wouldn't make a lot of sense, because during reading we don't know yet which class needs to be created. All we have is the element name.

@staabm
Copy link
Member

staabm commented Dec 2, 2015

Oh, obviously you are right ;-)

@staabm
Copy link
Member

staabm commented Dec 4, 2015

one thing which bugs me...

class ErpOrderService extends Sabre\Xml\Service {
    public $elementMap = array(
        '{http://b2bOrder.complex.de}order_response' => function (Sabre\Xml\Reader $reader) use ($popoDeserializer) {
            return $popoDeserializer($reader, new OrderResponse(), XMLNS_ORDER);
        },
        '{http://b2bOrder.complex.de}order_status' => function (Sabre\Xml\Reader $reader) use ($popoDeserializer) {
        return $popoDeserializer($reader, new OrderStatus(), XMLNS_ORDER);
        },
    );
}

is not valid php, as I cannot use closures on the public field here

@staabm
Copy link
Member

staabm commented Dec 4, 2015

ok, wil change it to

$erpOrderService = new Sabre\Xml\Service();
$erpOrderService->elementMap = array(
    '{'. XMLNS_ORDER .'}order_response' => function (Sabre\Xml\Reader $reader) use ($popoDeserializer) {
        return $popoDeserializer($reader, new OrderResponse(), XMLNS_ORDER);
    },
    '{'. XMLNS_ORDER .'}order_status' => function (Sabre\Xml\Reader $reader) use ($popoDeserializer) {
        return $popoDeserializer($reader, new OrderStatus(), XMLNS_ORDER);
    },
);

like the Reader/Writer examples

@staabm
Copy link
Member

staabm commented Dec 4, 2015

... progress...

$xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <order_response xmlns="http://b2bOrder.complex.de">
 <request_id>4711</request_id>
 <order_id>54752</order_id>
    <order_status xmlns="http://b2bOrder.complex.de">
         <status>INITIALIZED</status>
    </order_status>
 <success>true</success>
 </order_response>';

$popoDeserializer = function(Sabre\Xml\Reader $reader, $popo, $namespace)
{
    if ($reader->isEmptyElement) {
        throw new Exception(__CLASS__ . ' does not support empty elements');
    }

    $reader->read();
    do {

        if ($reader->nodeType === Sabre\Xml\Reader::ELEMENT && $reader->namespaceURI == $namespace) {

            $popo->{$reader->localName} = $reader->parseCurrentElement()['value'];
        } else {
            $reader->read();
        }
    } while ($reader->nodeType !== Sabre\Xml\Reader::END_ELEMENT);

    $reader->read();

    return $popo;
};

$popoSerializer = function(Sabre\Xml\Writer $writer, $popo, $namespace)  {
    foreach(get_object_vars($popo) as $key => $val) {
        $writer->writeElement('{'. $namespace .'}' . $key, $val);
    }
};

class OrderResponse
{
    public $request_id;
    public $order_id;
    public $order_status;
    public $success;
}

class OrderStatus
{
    public $status;
}

define("XMLNS_ORDER", "http://b2bOrder.complex.de");

$erpOrderService = new Sabre\Xml\Service();
$erpOrderService->namespaceMap[XMLNS_ORDER] = '';
// how to read xml into objects
$erpOrderService->elementMap = array(
    '{'. XMLNS_ORDER .'}order_response' => function (Sabre\Xml\Reader $reader) use ($popoDeserializer) {
        return $popoDeserializer($reader, new OrderResponse(), XMLNS_ORDER);
    },
    '{'. XMLNS_ORDER .'}order_status' => function (Sabre\Xml\Reader $reader) use ($popoDeserializer) {
        return $popoDeserializer($reader, new OrderStatus(), XMLNS_ORDER);
    },
);
// how to create xml from obejects
$erpOrderService->classMap = array(
    'OrderResponse' => function(Sabre\Xml\Writer $writer, $orderResponse) use ($popoSerializer) {
        /** @var $orderResponse OrderResponse */
        $popoSerializer($writer, $orderResponse, XMLNS_ORDER);
    },
    'OrderStatus' => function(Sabre\Xml\Writer $writer, $orderStatus) use ($popoSerializer) {
        /** @var $orderStatus OrderStatus */
        $popoSerializer($writer, $orderStatus, XMLNS_ORDER);
    }
);
var_dump($xml);
$orderResp = $erpOrderService->parse($xml);
var_dump($orderResp);
var_dump($erpOrderService->write(get_class($orderResp), $orderResp));

output

string(331) "<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <order_response xmlns="http://b2bOrder.complex.de">
 <request_id>4711</request_id>
 <order_id>54752</order_id>
    <order_status xmlns="http://b2bOrder.complex.de">
         <status>INITIALIZED</status>
    </order_status>
 <success>true</success>
 </order_response>"
object(OrderResponse)#11 (4) {
  ["request_id"]=>
  string(4) "4711"
  ["order_id"]=>
  string(5) "54752"
  ["order_status"]=>
  object(OrderStatus)#12 (1) {
    ["status"]=>
    string(11) "INITIALIZED"
  }
  ["success"]=>
  string(4) "true"
}
string(248) "<?xml version="1.0"?>
<OrderResponse xmlns="http://b2bOrder.complex.de">
 <:request_id>4711</:request_id>
 <:order_id>54752</:order_id>
 <:order_status>
  <:status>INITIALIZED</:status>
 </:order_status>
 <:success>true</:success>
</OrderResponse>
"

except the : in the written xml, it looks fine ;)

In contrast to your usual convention that a element should not emit the xml-element for itself, in case of having a external deserializer it would make sense to have the serializer emit the whole element... wdyt? (in case we agree here, I cannot use the service->write as the root element would then be emitted twice

@evert
Copy link
Member Author

evert commented Dec 4, 2015

as I cannot use closures on the public field here

Alternatively you could also have set them in the constructor.

except the : in the written xml, it looks fine ;)

What's with that :? That doesn't look right!

in case of having a external deserializer it would make sense to have the serializer emit the whole element... wdyt? (in case we agree here, I cannot use the service->write as the root element would then be emitted twice

Personally I would not break the rule, but if you think you have good reasons to do so, you should ;)

@staabm
Copy link
Member

staabm commented Dec 4, 2015

What's with that :? That doesn't look right!

I dont know why. The above code is runnable, maybe it helps to repro the problem

@staabm
Copy link
Member

staabm commented Dec 5, 2015

I guess the : are caused by the line $erpOrderService->namespaceMap[XMLNS_ORDER] = ''; (not tested yet).

I do this to not get a namespace (inspired by the docs from http://sabre.io/xml/writing/)

@evert
Copy link
Member Author

evert commented Dec 5, 2015

Can you try null instead of '' ?

@staabm
Copy link
Member

staabm commented Dec 7, 2015

@evert you are right, we need null instead of ''.

send a PR which fixes the example code.
sabre-io/sabre.io#61

It also fixes another error with the code.

So after all this we have a working code:

$xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <order_response xmlns="http://b2bOrder.complex.de">
 <request_id>4711</request_id>
 <order_id>54752</order_id>
    <order_status xmlns="http://b2bOrder.complex.de">
         <status>INITIALIZED</status>
    </order_status>
 <success>true</success>
 </order_response>';

$popoDeserializer = function(Sabre\Xml\Reader $reader, $popo, $namespace)
{
    if ($reader->isEmptyElement) {
        throw new Exception(__CLASS__ . ' does not support empty elements');
    }

    $reader->read();
    do {

        if ($reader->nodeType === Sabre\Xml\Reader::ELEMENT && $reader->namespaceURI == $namespace) {

            $popo->{$reader->localName} = $reader->parseCurrentElement()['value'];
        } else {
            $reader->read();
        }
    } while ($reader->nodeType !== Sabre\Xml\Reader::END_ELEMENT);

    $reader->read();

    return $popo;
};

$popoSerializer = function(Sabre\Xml\Writer $writer, $popo, $namespace)  {
    foreach(get_object_vars($popo) as $key => $val) {
        $writer->writeElement('{'. $namespace .'}' . $key, $val);
    }
};

class OrderResponse
{
    public $request_id;
    public $order_id;
    public $order_status;
    public $success;
}

class OrderStatus
{
    public $status;
}

define("XMLNS_ORDER", "http://b2bOrder.complex.de");

$erpOrderService = new Sabre\Xml\Service();
$erpOrderService->namespaceMap[XMLNS_ORDER] = null;
// how to read xml into objects
$erpOrderService->elementMap = array(
    '{'. XMLNS_ORDER .'}order_response' => function (Sabre\Xml\Reader $reader) use ($popoDeserializer) {
        return $popoDeserializer($reader, new OrderResponse(), XMLNS_ORDER);
    },
    '{'. XMLNS_ORDER .'}order_status' => function (Sabre\Xml\Reader $reader) use ($popoDeserializer) {
        return $popoDeserializer($reader, new OrderStatus(), XMLNS_ORDER);
    },
);
// how to create xml from obejects
$erpOrderService->classMap = array(
    'OrderResponse' => function(Sabre\Xml\Writer $writer, $orderResponse) use ($popoSerializer) {
        /** @var $orderResponse OrderResponse */
        $popoSerializer($writer, $orderResponse, XMLNS_ORDER);
    },
    'OrderStatus' => function(Sabre\Xml\Writer $writer, $orderStatus) use ($popoSerializer) {
        /** @var $orderStatus OrderStatus */
        $popoSerializer($writer, $orderStatus, XMLNS_ORDER);
    }
);

echo "<xmp>";
var_dump($xml);
$orderResp = $erpOrderService->parse($xml);
var_dump($orderResp);
var_dump($erpOrderService->write(get_class($orderResp), $orderResp));

output

string(323) "<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <order_response xmlns="http://b2bOrder.complex.de">
 <request_id>4711</request_id>
 <order_id>54752</order_id>
    <order_status xmlns="http://b2bOrder.complex.de">
         <status>INITIALIZED</status>
    </order_status>
 <success>true</success>
 </order_response>"
object(OrderResponse)#10 (4) {
  ["request_id"]=>
  string(4) "4711"
  ["order_id"]=>
  string(5) "54752"
  ["order_status"]=>
  object(OrderStatus)#11 (1) {
    ["status"]=>
    string(11) "INITIALIZED"
  }
  ["success"]=>
  string(4) "true"
}
string(238) "<?xml version="1.0"?>
<OrderResponse xmlns="http://b2bOrder.complex.de">
 <request_id>4711</request_id>
 <order_id>54752</order_id>
 <order_status>
  <status>INITIALIZED</status>
 </order_status>
 <success>true</success>
</OrderResponse>
"

do you think this kind of popo serializer/deserializer is something which should be shipped with sabre/xml or is it to specific for my current app?

@staabm staabm mentioned this pull request Dec 7, 2015
@evert evert added this to the 1.3 milestone Dec 8, 2015
@evert evert self-assigned this Dec 8, 2015
@evert
Copy link
Member Author

evert commented Dec 8, 2015

I would definitely think it's a good idea to have this as a core feature btw!

evert added a commit that referenced this pull request Dec 8, 2015
@evert evert merged commit c82978b into master Dec 8, 2015
@evert
Copy link
Member Author

evert commented Dec 8, 2015

Perhaps we can integrate this in the Service class with a function that takes a class name and xml element name and automatically registers both the serializer and deserializer.

@staabm
Copy link
Member

staabm commented Dec 8, 2015

Agree that having it in the Service class is a good idea. Its the place where this kind of api-sugar should happen. PR incoming

@evert evert deleted the external-object-serializers branch December 8, 2015 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants