【问题标题】:Serialize XML document with attributes使用属性序列化 XML 文档
【发布时间】:2015-03-02 21:05:16
【问题描述】:

我正在尝试序列化包含要插入 Doctrine MySQL 数据库的实体的 XML 文档。

例如,我的实体中有这两个属性:

  • $companyId
  • $companyName

问题是我的 XML 文档中没有这样的内容:

<company>
    <id>8888</id>
    <name>MyCompany</name>
</company>

我得到了这样的东西:

<company id="8888" name="MyCompany"/>

XML 是由我与之合作的一家独立公司生成的;所以我无法改变它。 所以 Symfony2 序列化程序正在创建一个空的 $company 属性:(

有没有一种简单的方法可以像我想要的那样定制序列化过程?还是我必须实现一个完全独立的方法?

非常感谢。

【问题讨论】:

    标签: php xml symfony serialization


    【解决方案1】:

    我会创建一个简单的Denormalizer,因为默认情况下已经解析了属性XmlEncoder。它在键的开头添加了一个特殊字符@

    无需进行大量调整,您就可以添加一个上下文参数,例如 use_attributes,您的自定义反规范化器可以理解该参数。这是一个例子

    use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
    
    class AttributesDenormalizer implements DenormalizerInterface
    {
        public function __construct(DenormalizerInterface $delegate)
        {
            $this->delegate = $delegate;
        }
    
        public function denormalize($data, $class, $format = null, array $context = array())
        {
            if (!is_array($data) || !isset($context['use_attributes']) || true !== $context['use_attributes']) {
                return $this->delegate->denormalize($data, $class, $format, $context);
            }
    
            $attributes = array();
    
            foreach ($data as $key => $value) {
                if (0 === strpos($key, '@')) {
                    $attributes[substr($key, 1)] = $value;
                }
            }
    
            if (empty($attributes)) {
                $attributes = $data;
            }
    
            return $this->delegate->denormalize($attributes, $class, $format, $context);
        }
    
        public function supportsDenormalization($data, $type, $format = null)
        {
            return $this->delegate->supportsDenormalization($data, $type, $format);
        }
    }
    

    这是一个使用示例

    use Symfony\Component\Serializer\Serializer;
    use Symfony\Component\Serializer\Encoder\XmlEncoder;
    use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
    
    $xml = '<company id="8888" name="MyCompany"/>';
    
    $encoders = array(new XmlEncoder());
    $normalizers = array(new AttributesDenormalizer(new GetSetMethodNormalizer));
    
    $serializer = new Serializer($normalizers, $encoders);
    $serializer->deserialize($xml, 'Company', 'xml', array('use_attributes' => true));
    

    结果

    class Company#13 (2) {
      protected $id =>
      string(4) "8888"
      protected $name =>
      string(9) "MyCompany"
    }
    

    【讨论】:

    • 好的,我明白了。但这有点复杂,因为我可以在同一个 XML 文件中有多个 &lt;company&gt;,而且我还必须管理关系......我想我必须使用 JMSSerializerBundle!
    • jms/serializer 对于更复杂的情况(对象数组、深度序列化......)确实是一个不错的选择。但是,使用给出的示例可以轻松管理简单的对象数组。
    • 是的,非常感谢你的例子;我正在尝试几种解决方案,我会告诉你哪个是最好的。
    • 好的,我刚刚查看了 JMSSerializerBundle。它允许通过注释系统做很多事情,但它不能原生解决我的问题......我想我必须创建自己的处理程序来修复它。
    【解决方案2】:

    现在非常通过使用带有“@”的@SerializeName 注解来更容易序列化 XML 属性。

    在您的公司实体中,定义 $name 时,添加

        /**
         * @ORM\Column(type="string", length=255)
         * @SerializedName('@name')
         */
        private $name;
    

    现在,当您序列化为 XML 时,它将作为一个属性出现,正如预期的那样。

    我知道 OP 实际上是在询问反序列化,但希望这对正在搜索的人有所帮助。

    【讨论】:

      【解决方案3】:

      好的,最后我尝试使用 JMSSerializerBundle,但我的情况太复杂了。我得到了许多具有多个 ManyToOne 关系的实体;我的 XML 中有机器人标准和属性值。

      所以我会使用你的想法:创建我完整的整个 Denormalizer。它将使用解码的 XML 并逐行读取它,做它必须做的事情(使用 Doctrine 创建实体)。

      这将是一个巨大的过程,但却是最简单的一个。

      谢谢。

      [编辑] 我终于找到了一个很好的解决方案。 我将 XML 和我的实体设置器之间的链接注册到 yaml 表中

      company:
          @id: setCompanyId
          @name : setCompanyName
          address:
              @city: setAddressCity
              @street: setAddressStreet
      ...
      

      多亏了这一点,我可以轻松读取整个 XML,并且对于每个节点/属性值,在表中找到设置器名称,然后执行:

      $company = new Company;
      $company->setterNameFromTable($value);
      

      【讨论】:

        猜你喜欢
        • 2020-08-28
        • 2013-08-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多