Update to Drupal 8.1.0. For more information, see https://www.drupal.org/drupal-8.1.0-release-notes

This commit is contained in:
Pantheon Automation 2016-04-20 09:56:34 -07:00 committed by Greg Anderson
parent b11a755ba8
commit c0a0d5a94c
6920 changed files with 64395 additions and 57312 deletions

View file

@ -108,7 +108,7 @@ class JsonDecode implements DecoderInterface
}
if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) {
throw new UnexpectedValueException(JsonEncoder::getLastErrorMessage());
throw new UnexpectedValueException(json_last_error_msg());
}
return $decodedData;

View file

@ -56,7 +56,7 @@ class JsonEncode implements EncoderInterface
$encodedJson = json_encode($data, $context['json_encode_options']);
if (JSON_ERROR_NONE !== $this->lastError = json_last_error()) {
throw new UnexpectedValueException(JsonEncoder::getLastErrorMessage());
throw new UnexpectedValueException(json_last_error_msg());
}
return $encodedJson;

View file

@ -100,26 +100,13 @@ class JsonEncoder implements EncoderInterface, DecoderInterface
* Resolves json_last_error message.
*
* @return string
*
* @deprecated since 2.8, to be removed in 3.0. Use json_last_error_msg() instead.
*/
public static function getLastErrorMessage()
{
if (function_exists('json_last_error_msg')) {
return json_last_error_msg();
}
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use json_last_error_msg() instead.', E_USER_DEPRECATED);
switch (json_last_error()) {
case JSON_ERROR_DEPTH:
return 'Maximum stack depth exceeded';
case JSON_ERROR_STATE_MISMATCH:
return 'Underflow or the modes mismatch';
case JSON_ERROR_CTRL_CHAR:
return 'Unexpected control character found';
case JSON_ERROR_SYNTAX:
return 'Syntax error, malformed JSON';
case JSON_ERROR_UTF8:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
default:
return 'Unknown error';
}
return json_last_error_msg();
}
}

View file

@ -366,7 +366,7 @@ class XmlEncoder extends SerializerAwareEncoder implements EncoderInterface, Dec
{
$append = true;
if (is_array($data) || $data instanceof \Traversable) {
if (is_array($data) || ($data instanceof \Traversable && !$this->serializer->supportsNormalization($data, $this->format))) {
foreach ($data as $key => $data) {
//Ah this is the magic @ attribute types.
if (0 === strpos($key, '@') && is_scalar($data) && $this->isElementNameValid($attributeName = substr($key, 1))) {

View file

@ -0,0 +1,16 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\Exception;
class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
{
}

View file

@ -1,4 +1,4 @@
Copyright (c) 2004-2015 Fabien Potencier
Copyright (c) 2004-2016 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -16,6 +16,8 @@ namespace Symfony\Component\Serializer\Mapping;
*
* Primarily, the metadata stores serialization groups.
*
* @internal
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface AttributeMetadataInterface

View file

@ -14,7 +14,11 @@ namespace Symfony\Component\Serializer\Mapping;
/**
* Stores metadata needed for serializing and deserializing objects of specific class.
*
* Primarily, the metadata stores the list of attributes to serialize or deserialize.
* Primarily, the metadata stores the set of attributes to serialize or deserialize.
*
* There may only exist one metadata for each attribute according to its name.
*
* @internal
*
* @author Kévin Dunglas <dunglas@gmail.com>
*/

View file

@ -27,10 +27,12 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
* @var LoaderInterface
*/
private $loader;
/**
* @var Cache
*/
private $cache;
/**
* @var array
*/

View file

@ -65,6 +65,7 @@ class YamlFileLoader extends FileLoader
if (isset($yaml['attributes']) && is_array($yaml['attributes'])) {
$attributesMetadata = $classMetadata->getAttributesMetadata();
foreach ($yaml['attributes'] as $attribute => $data) {
if (isset($attributesMetadata[$attribute])) {
$attributeMetadata = $attributesMetadata[$attribute];
@ -74,7 +75,15 @@ class YamlFileLoader extends FileLoader
}
if (isset($data['groups'])) {
if (!is_array($data['groups'])) {
throw new MappingException('The "groups" key must be an array of strings in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName());
}
foreach ($data['groups'] as $group) {
if (!is_string($group)) {
throw new MappingException('Group names must be strings in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName());
}
$attributeMetadata->addGroup($group);
}
}

View file

@ -22,6 +22,7 @@ class CamelCaseToSnakeCaseNameConverter implements NameConverterInterface
* @var array|null
*/
private $attributes;
/**
* @var bool
*/

View file

@ -27,30 +27,40 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
*/
abstract class AbstractNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface
{
const CIRCULAR_REFERENCE_LIMIT = 'circular_reference_limit';
const OBJECT_TO_POPULATE = 'object_to_populate';
const GROUPS = 'groups';
/**
* @var int
*/
protected $circularReferenceLimit = 1;
/**
* @var callable
*/
protected $circularReferenceHandler;
/**
* @var ClassMetadataFactoryInterface|null
*/
protected $classMetadataFactory;
/**
* @var NameConverterInterface|null
*/
protected $nameConverter;
/**
* @var array
*/
protected $callbacks = array();
/**
* @var array
*/
protected $ignoredAttributes = array();
/**
* @var array
*/
@ -185,16 +195,16 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
{
$objectHash = spl_object_hash($object);
if (isset($context['circular_reference_limit'][$objectHash])) {
if ($context['circular_reference_limit'][$objectHash] >= $this->circularReferenceLimit) {
unset($context['circular_reference_limit'][$objectHash]);
if (isset($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash])) {
if ($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash] >= $this->circularReferenceLimit) {
unset($context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash]);
return true;
}
++$context['circular_reference_limit'][$objectHash];
++$context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash];
} else {
$context['circular_reference_limit'][$objectHash] = 1;
$context[static::CIRCULAR_REFERENCE_LIMIT][$objectHash] = 1;
}
return false;
@ -248,18 +258,18 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
*/
protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false)
{
if (!$this->classMetadataFactory || !isset($context['groups']) || !is_array($context['groups'])) {
if (!$this->classMetadataFactory || !isset($context[static::GROUPS]) || !is_array($context[static::GROUPS])) {
return false;
}
$allowedAttributes = array();
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
if (count(array_intersect($attributeMetadata->getGroups(), $context['groups']))) {
if (count(array_intersect($attributeMetadata->getGroups(), $context[static::GROUPS]))) {
$allowedAttributes[] = $attributesAsString ? $attributeMetadata->getName() : $attributeMetadata;
}
}
return array_unique($allowedAttributes);
return $allowedAttributes;
}
/**
@ -279,7 +289,9 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
* Instantiates an object using constructor parameters when needed.
*
* This method also allows to denormalize data into an existing object if
* it is present in the context with the object_to_populate key.
* it is present in the context with the object_to_populate. This object
* is removed from the context before being returned to avoid side effects
* when recursively normalizing an object graph.
*
* @param array $data
* @param string $class
@ -294,11 +306,14 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N
protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes)
{
if (
isset($context['object_to_populate']) &&
is_object($context['object_to_populate']) &&
$class === get_class($context['object_to_populate'])
isset($context[static::OBJECT_TO_POPULATE]) &&
is_object($context[static::OBJECT_TO_POPULATE]) &&
$context[static::OBJECT_TO_POPULATE] instanceof $class
) {
return $context['object_to_populate'];
$object = $context[static::OBJECT_TO_POPULATE];
unset($context[static::OBJECT_TO_POPULATE]);
return $object;
}
$constructor = $reflectionClass->getConstructor();

View file

@ -0,0 +1,77 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\Serializer\Exception\BadMethodCallException;
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Denormalizes arrays of objects.
*
* @author Alexander M. Turek <me@derrabus.de>
*/
class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterface
{
/**
* @var SerializerInterface|DenormalizerInterface
*/
private $serializer;
/**
* {@inheritdoc}
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
if ($this->serializer === null) {
throw new BadMethodCallException('Please set a serializer before calling denormalize()!');
}
if (!is_array($data)) {
throw new InvalidArgumentException('Data expected to be an array, '.gettype($data).' given.');
}
if (substr($class, -2) !== '[]') {
throw new InvalidArgumentException('Unsupported class: '.$class);
}
$serializer = $this->serializer;
$class = substr($class, 0, -2);
return array_map(
function ($data) use ($serializer, $class, $format, $context) {
return $serializer->denormalize($data, $class, $format, $context);
},
$data
);
}
/**
* {@inheritdoc}
*/
public function supportsDenormalization($data, $type, $format = null)
{
return substr($type, -2) === '[]'
&& $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format);
}
/**
* {@inheritdoc}
*/
public function setSerializer(SerializerInterface $serializer)
{
if (!$serializer instanceof DenormalizerInterface) {
throw new InvalidArgumentException('Expected a serializer that also implements DenormalizerInterface.');
}
$this->serializer = $serializer;
}
}

View file

@ -59,8 +59,10 @@ class CustomNormalizer extends SerializerAwareNormalizer implements NormalizerIn
*/
public function supportsDenormalization($data, $type, $format = null)
{
$class = new \ReflectionClass($type);
if (!class_exists($type)) {
return false;
}
return $class->isSubclassOf('Symfony\Component\Serializer\Normalizer\DenormalizableInterface');
return is_subclass_of($type, 'Symfony\Component\Serializer\Normalizer\DenormalizableInterface');
}
}

View file

@ -114,7 +114,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
if ($allowed && !$ignored) {
$setter = 'set'.ucfirst($attribute);
if (in_array($setter, $classMethods)) {
if (in_array($setter, $classMethods) && !$reflectionClass->getMethod($setter)->isStatic()) {
$object->$setter($value);
}
}
@ -136,7 +136,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer
*/
public function supportsDenormalization($data, $type, $format = null)
{
return $this->supports($type);
return class_exists($type) && $this->supports($type);
}
/**
@ -170,10 +170,13 @@ class GetSetMethodNormalizer extends AbstractNormalizer
{
$methodLength = strlen($method->name);
return (
((0 === strpos($method->name, 'get') && 3 < $methodLength) ||
(0 === strpos($method->name, 'is') && 2 < $methodLength)) &&
0 === $method->getNumberOfRequiredParameters()
);
return
!$method->isStatic() &&
(
((0 === strpos($method->name, 'get') && 3 < $methodLength) ||
(0 === strpos($method->name, 'is') && 2 < $methodLength)) &&
0 === $method->getNumberOfRequiredParameters()
)
;
}
}

View file

@ -26,6 +26,8 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
*/
class ObjectNormalizer extends AbstractNormalizer
{
private $attributesCache = array();
/**
* @var PropertyAccessorInterface
*/
@ -53,44 +55,15 @@ class ObjectNormalizer extends AbstractNormalizer
*/
public function normalize($object, $format = null, array $context = array())
{
if (!isset($context['cache_key'])) {
$context['cache_key'] = $this->getCacheKey($context);
}
if ($this->isCircularReference($object, $context)) {
return $this->handleCircularReference($object);
}
$data = array();
$attributes = $this->getAllowedAttributes($object, $context, true);
// If not using groups, detect manually
if (false === $attributes) {
$attributes = array();
// methods
$reflClass = new \ReflectionClass($object);
foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) {
if (
!$reflMethod->isConstructor() &&
!$reflMethod->isDestructor() &&
0 === $reflMethod->getNumberOfRequiredParameters()
) {
$name = $reflMethod->getName();
if (strpos($name, 'get') === 0 || strpos($name, 'has') === 0) {
// getters and hassers
$attributes[lcfirst(substr($name, 3))] = true;
} elseif (strpos($name, 'is') === 0) {
// issers
$attributes[lcfirst(substr($name, 2))] = true;
}
}
}
// properties
foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) {
$attributes[$reflProperty->getName()] = true;
}
$attributes = array_keys($attributes);
}
$attributes = $this->getAttributes($object, $context);
foreach ($attributes as $attribute) {
if (in_array($attribute, $this->ignoredAttributes)) {
@ -134,6 +107,9 @@ class ObjectNormalizer extends AbstractNormalizer
*/
public function denormalize($data, $class, $format = null, array $context = array())
{
if (!isset($context['cache_key'])) {
$context['cache_key'] = $this->getCacheKey($context);
}
$allowedAttributes = $this->getAllowedAttributes($class, $context, true);
$normalizedData = $this->prepareForDenormalization($data);
@ -159,4 +135,95 @@ class ObjectNormalizer extends AbstractNormalizer
return $object;
}
private function getCacheKey(array $context)
{
try {
return md5(serialize($context));
} catch (\Exception $exception) {
// The context cannot be serialized, skip the cache
return false;
}
}
/**
* Gets and caches attributes for this class and context.
*
* @param object $object
* @param array $context
*
* @return string[]
*/
private function getAttributes($object, array $context)
{
$class = get_class($object);
$key = $class.'-'.$context['cache_key'];
if (isset($this->attributesCache[$key])) {
return $this->attributesCache[$key];
}
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
if (false !== $allowedAttributes) {
if ($context['cache_key']) {
$this->attributesCache[$key] = $allowedAttributes;
}
return $allowedAttributes;
}
if (isset($this->attributesCache[$class])) {
return $this->attributesCache[$class];
}
return $this->attributesCache[$class] = $this->extractAttributes($object);
}
/**
* Extracts attributes for this class and context.
*
* @param object $object
*
* @return string[]
*/
private function extractAttributes($object)
{
// If not using groups, detect manually
$attributes = array();
// methods
$reflClass = new \ReflectionClass($object);
foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) {
if (
$reflMethod->getNumberOfRequiredParameters() !== 0 ||
$reflMethod->isStatic() ||
$reflMethod->isConstructor() ||
$reflMethod->isDestructor()
) {
continue;
}
$name = $reflMethod->name;
if (0 === strpos($name, 'get') || 0 === strpos($name, 'has')) {
// getters and hassers
$attributes[lcfirst(substr($name, 3))] = true;
} elseif (strpos($name, 'is') === 0) {
// issers
$attributes[lcfirst(substr($name, 2))] = true;
}
}
// properties
foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) {
if ($reflProperty->isStatic()) {
continue;
}
$attributes[$reflProperty->name] = true;
}
return array_keys($attributes);
}
}

View file

@ -50,7 +50,7 @@ class PropertyNormalizer extends AbstractNormalizer
$allowedAttributes = $this->getAllowedAttributes($object, $context, true);
foreach ($reflectionObject->getProperties() as $property) {
if (in_array($property->name, $this->ignoredAttributes)) {
if (in_array($property->name, $this->ignoredAttributes) || $property->isStatic()) {
continue;
}
@ -110,6 +110,10 @@ class PropertyNormalizer extends AbstractNormalizer
if ($allowed && !$ignored && $reflectionClass->hasProperty($propertyName)) {
$property = $reflectionClass->getProperty($propertyName);
if ($property->isStatic()) {
continue;
}
// Override visibility
if (!$property->isPublic()) {
$property->setAccessible(true);
@ -135,7 +139,7 @@ class PropertyNormalizer extends AbstractNormalizer
*/
public function supportsDenormalization($data, $type, $format = null)
{
return $this->supports($type);
return class_exists($type) && $this->supports($type);
}
/**

View file

@ -1,15 +1,16 @@
Serializer Component
====================
With the Serializer component it's possible to handle serializing data structures,
including object graphs, into array structures or other formats like XML and JSON.
It can also handle deserializing XML and JSON back to object graphs.
With the Serializer component it's possible to handle serializing data
structures, including object graphs, into array structures or other formats like
XML and JSON. It can also handle deserializing XML and JSON back to object
graphs.
Resources
---------
You can run the unit tests with the following command:
$ cd path/to/Symfony/Component/Serializer/
$ composer install
$ phpunit
* [Documentation](https://symfony.com/doc/current/components/serializer.html)
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)

View file

@ -41,18 +41,22 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
* @var Encoder\ChainEncoder
*/
protected $encoder;
/**
* @var Encoder\ChainDecoder
*/
protected $decoder;
/**
* @var array
*/
protected $normalizers = array();
/**
* @var array
*/
protected $normalizerCache = array();
/**
* @var array
*/
@ -127,10 +131,8 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
if (null === $data || is_scalar($data)) {
return $data;
}
if (is_object($data) && $this->supportsNormalization($data, $format)) {
return $this->normalizeObject($data, $format, $context);
}
if ($data instanceof \Traversable) {
if (is_array($data) || $data instanceof \Traversable) {
$normalized = array();
foreach ($data as $key => $val) {
$normalized[$key] = $this->normalize($val, $format, $context);
@ -138,16 +140,15 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
return $normalized;
}
if (is_object($data)) {
return $this->normalizeObject($data, $format, $context);
}
if (is_array($data)) {
foreach ($data as $key => $val) {
$data[$key] = $this->normalize($val, $format, $context);
if (!$this->normalizers) {
throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
}
return $data;
throw new UnexpectedValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', get_class($data)));
}
throw new UnexpectedValueException(sprintf('An unexpected value could not be normalized: %s', var_export($data, true)));
}
@ -185,19 +186,8 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
private function getNormalizer($data, $format)
{
if ($isObject = is_object($data)) {
$class = get_class($data);
if (isset($this->normalizerCache[$class][$format])) {
return $this->normalizerCache[$class][$format];
}
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format)) {
if ($isObject) {
$this->normalizerCache[$class][$format] = $normalizer;
}
return $normalizer;
}
}
@ -214,14 +204,8 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
*/
private function getDenormalizer($data, $class, $format)
{
if (isset($this->denormalizerCache[$class][$format])) {
return $this->denormalizerCache[$class][$format];
}
foreach ($this->normalizers as $normalizer) {
if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format)) {
$this->denormalizerCache[$class][$format] = $normalizer;
return $normalizer;
}
}
@ -243,30 +227,6 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
return $this->decoder->decode($data, $format, $context);
}
/**
* Normalizes an object into a set of arrays/scalars.
*
* @param object $object object to normalize
* @param string $format format name, present to give the option to normalizers to act differently based on formats
* @param array $context The context data for this particular normalization
*
* @return array|string|bool|int|float|null
*
* @throws LogicException
* @throws UnexpectedValueException
*/
private function normalizeObject($object, $format, array $context = array())
{
if (!$this->normalizers) {
throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
}
if ($normalizer = $this->getNormalizer($object, $format)) {
return $normalizer->normalize($object, $format, $context);
}
throw new UnexpectedValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', get_class($object)));
}
/**
* Denormalizes data back into an object of the given class.
*
@ -289,6 +249,16 @@ class Serializer implements SerializerInterface, NormalizerInterface, Denormaliz
if ($normalizer = $this->getDenormalizer($data, $class, $format)) {
return $normalizer->denormalize($data, $class, $format, $context);
}
foreach ($this->normalizers as $normalizer) {
if (
$normalizer instanceof DenormalizerInterface &&
$normalizer->supportsDenormalization($data, $class, $format)
) {
return $normalizer->denormalize($data, $class, $format, $context);
}
}
throw new UnexpectedValueException(sprintf('Could not denormalize object of type %s, no supporting normalizer found.', $class));
}

View file

@ -16,12 +16,13 @@
}
],
"require": {
"php": ">=5.3.9"
"php": ">=5.3.9",
"symfony/polyfill-php55": "~1.0"
},
"require-dev": {
"symfony/yaml": "~2.0,>=2.0.5",
"symfony/config": "~2.2",
"symfony/property-access": "~2.3",
"symfony/yaml": "~2.0,>=2.0.5|~3.0.0",
"symfony/config": "~2.2|~3.0.0",
"symfony/property-access": "~2.3|~3.0.0",
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.0"
},
@ -33,12 +34,15 @@
"symfony/property-access": "For using the ObjectNormalizer."
},
"autoload": {
"psr-4": { "Symfony\\Component\\Serializer\\": "" }
"psr-4": { "Symfony\\Component\\Serializer\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
"dev-master": "2.8-dev"
}
}
}

View file

@ -20,8 +20,8 @@
<whitelist>
<directory>./</directory>
<exclude>
<directory>./vendor</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>