Closed evert closed 8 years ago
Any chanche we can get the classmap beeing used for reading and writing?
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.
Oh, obviously you are right ;-)
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
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
... 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
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 ;)
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
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/)
Can you try null
instead of ''
?
@evert you are right, we need null
instead of ''
.
send a PR which fixes the example code. https://github.com/fruux/sabre.io/pull/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?
I would definitely think it's a good idea to have this as a core feature btw!
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.
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
cc: @staabm. What do you think?