我正在迁移Zend Framework 3应用程序的ZendDb
驱动DBAL到学说。一切都很好,但是现在我对数据导出遇到了问题。
在迁移之前,它的工作原理如下:
有一个或多或少复杂的数据结构。Mapper
执行了一些数据库请求,并通过此数据构建了一个嵌套的DataObject
。因此,导出的起点是一个对象,充满了所有数据并具有子对象,以及所有数据。因此,我只是将其转换为JSON
:
public function exportToJson(AbstractDataObject $dataObject)
{
return json_encode($dataObject, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
public function exportToXml(AbstractDataObject $dataObject)
{
$dataObjectVars = json_decode(json_encode($dataObject->jsonSerialize()), true);
$xml = new SimpleXMLElement('<' . self::XML_DEFAULT_ROOT_ELEMENT . ' />');
$this->arrayToXml($dataObjectVars, $xml);
$domxml = new DOMDocument('1.0');
$domxml->preserveWhiteSpace = false;
$domxml->formatOutput = true;
$domxml->loadXML($xml->asXML());
$xmlString = $domxml->saveXML();
return $xmlString;
}
protected function arrayToXml($array, &$xml)
{
foreach ($array as $key => $value) {
if(is_array($value)){
if(is_int($key)){
$key = self::XML_DEFAULT_ELEMENT_NAME;
}
$label = $xml->addChild($key);
$this->arrayToXml($value, $label);
}
else {
$xml->addChild($key, $value);
}
}
}
所有DataObject
S扩展了AbstractDataObject
,并提供了一种方法,使其易于导出到JSON
:
class AbstractDataObject implements JsonSerializable
{
public function jsonSerialize()
{
$reflection = new ReflectionClass($this);
$properties = $reflection->getProperties();
$members = [];
foreach ($properties as $property) {
$property->setAccessible(true);
$members[$property->getName()] = $property->getValue($this);
}
$keys = array_keys($members);
$values = array_values($members);
$keysUnderscored = preg_replace_callback('/([A-Z])/', function($matches) {
return '_' . strtolower($matches[1]);
}, $keys);
$varsUnderscored = array_combine($keysUnderscored, $values);
return $varsUnderscored;
}
}
现在,导出的对象是一个实体,通常没有加载其所有数据。这意味着,上述方法不再起作用。
是否存在/什么是将嵌套实体(意味着具有子本性的实体)转换为结构化数据格式(array
/JSON
/XML
)的正确方法?
最后,我正如塞拉德(Cerad)对Symfony Serialializer组件的评论所建议的。
我对编码遇到了一些麻烦:JSON的JSON_ERROR_UTF8
和XML的" Warning: DOMDocument::saveXML(): invalid character value
"。因此,我必须" UTF8ize"从Serializer
接收到的数组数据,并通过使用dom_import_simplexml(...)
重新进来exportToXml(...)
,如下所示。但是现在它正在奏效,我们去这里:
namespace MyNamespaceDataExport;
use MyNamespaceDataObjectAbstractDataObject;
use SymfonyComponentSerializerNameConverterCamelCaseToSnakeCaseNameConverter;
use SymfonyComponentSerializerNormalizerObjectNormalizer;
use SymfonyComponentSerializerSerializer;
use SymfonyComponentSerializerMappingFactoryClassMetadataFactory;
use DoctrineCommonAnnotationsAnnotationReader;
use SymfonyComponentSerializerMappingLoaderAnnotationLoader;
class DataExporter
{
/** @var string */
const EXPORT_FORMAT_JSON = 'json';
/** @var string */
const EXPORT_FORMAT_XML = 'xml';
/** @var string */
const XML_DEFAULT_ROOT_ELEMENT = 'my_root_element_name';
/** @var string */
const XML_DEFAULT_ELEMENT_NAME = 'item';
/** @var Serializer */
protected $serializer;
public function __construct()
{
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter());
$normalizer->setCircularReferenceLimit(1);
$normalizer->setIgnoredAttributes(['__initializer__', '__cloner__', '__isInitialized__']);
$normalizer->setCircularReferenceHandler(function ($object) {
// @todo A cleaner solution need.
try {
$return = $object->getId();
} catch (Error $exception) {
$return = null;
}
$return = null;
return $return;
});
$normalizers = [$normalizer];
$this->serializer = new Serializer($normalizers);
}
public function exportToJson(AbstractDataObject $dataObject)
{
$data = $this->serializer->normalize($dataObject, null, ['groups' => ['export']]);
$data = $this->utf8ize($data);
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
public function exportToXml(AbstractDataObject $dataObject)
{
$data = $this->serializer->normalize($dataObject, null, ['groups' => ['export']]);
$data = $this->utf8ize($data);
$xml = new SimpleXMLElement('<' . self::XML_DEFAULT_ROOT_ELEMENT . ' />');
$this->arrayToXml($data, $xml);
$domDocument = dom_import_simplexml($xml)->ownerDocument;
$domDocument->formatOutput = true;
$xmlString = $domDocument->saveXML();
return $xmlString;
}
protected function utf8ize($data) {
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[$key] = $this->utf8ize($value);
}
} else if (is_string ($data)) {
return utf8_encode($data);
}
return $data;
}
/**
* Converts an $array to XML and
* saves the result to the $xml argument.
*
* @param array $array
* @param SimpleXMLElement $xml
* @return void
*/
protected function arrayToXml($array, &$xml){
foreach ($array as $key => $value) {
if(is_array($value)){
if(is_int($key)){
$key = self::XML_DEFAULT_ELEMENT_NAME;
}
$label = $xml->addChild($key);
$this->arrayToXml($value, $label);
}
else {
$xml->addChild($key, $value);
}
}
}
}