From e61434f9f1a65e918aff46cb8bcb4e79e0f20589 Mon Sep 17 00:00:00 2001 From: Peter Grundmann Date: Wed, 7 Aug 2024 20:41:09 +0200 Subject: [PATCH] - default datetime format is now W3C (like "1981-02-04T16:17:18+02:00") - JsonSettings now union of JsonSerializeSettings and JsonDeserializeSettings - new Setting "SkipNull" - new Setting "SkipEmptyArray" --- .gitattributes | 3 +- composer.json | 2 +- demos/Models/TypesTest.php | 6 ++- demos/data/types.json | 2 +- demos/enums.php | 7 ++- demos/types.php | 11 ++-- readme.md | 14 +++-- src/BaseExecuter.php | 2 +- src/JsonDeserializeSettings.php | 10 ---- src/JsonDeserializer.php | 8 --- src/JsonSerializeSettings.php | 59 --------------------- src/JsonSerializer.php | 11 ++-- src/JsonSettings.php | 91 +++++++++++++++++++++++++++++++-- 13 files changed, 119 insertions(+), 107 deletions(-) delete mode 100644 src/JsonDeserializeSettings.php delete mode 100644 src/JsonSerializeSettings.php diff --git a/.gitattributes b/.gitattributes index ef86f27..d192f2d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ -demos/ export-ignore \ No newline at end of file +demos/ export-ignore +.gitattributes \ No newline at end of file diff --git a/composer.json b/composer.json index d1c6bca..94cb6e9 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pyther/json", - "description": "JSON to/from model (de)serialization", + "description": "A lightweight (de)serializer between json strings and data models.", "type": "library", "keywords": [ "JSON", diff --git a/demos/Models/TypesTest.php b/demos/Models/TypesTest.php index 9f78f20..373e5f8 100644 --- a/demos/Models/TypesTest.php +++ b/demos/Models/TypesTest.php @@ -56,8 +56,12 @@ class TypesTest { * @var integer|null */ public $intNullableByHint; - #[JsonType(\int::class)] public $intByMeta; + + public ?string $nullValue = null; + + #[JsonType(\int::class)] + public array $emptyArray = []; } \ No newline at end of file diff --git a/demos/data/types.json b/demos/data/types.json index e9fc9b7..f5af2a3 100644 --- a/demos/data/types.json +++ b/demos/data/types.json @@ -6,7 +6,7 @@ "StringPropertyNullable": "abc", "IntOrStringProperty": "abc123", "IntOrStringPropertyNullable": "abc123", - "DateTimeProperty": "1981-02-04T16:17:18+", + "DateTimeProperty": "1981-02-04T16:17:18+02:00", "DateTimePropertyByMeta": "04\/02\/1981", "OrderItem": { "Sku": "123-456-789", diff --git a/demos/enums.php b/demos/enums.php index 80aa703..b7c70ad 100644 --- a/demos/enums.php +++ b/demos/enums.php @@ -3,9 +3,8 @@ use JsonException; use Pyther\Json\Json; -use Pyther\Json\JsonSerializeSettings; use Pyther\Json\Attributes\JsonEnum; -use Pyther\Json\JsonDeserializeSettings; +use Pyther\Json\JsonSettings; use Pyther\Json\NamingPolicies\CamelToPascalNamingPolicy; use Pyther\Json\Types\EnumFormat; @@ -89,14 +88,14 @@ class EnumTest { } function serializeTest($object): string { - $settings = new JsonSerializeSettings(); + $settings = new JsonSettings(); $settings->setEnumFormat(EnumFormat::Name); $settings->setNamingPolicy(new CamelToPascalNamingPolicy()); return Json::serialize($object, $settings); } function deserializeTest(string $json): EnumTest { - $settings = new JsonDeserializeSettings(); + $settings = new JsonSettings(); $settings->setNamingPolicy(new CamelToPascalNamingPolicy()); return Json::deserialize($json, EnumTest::class, $settings); } diff --git a/demos/types.php b/demos/types.php index c8b15b4..1d1c96c 100644 --- a/demos/types.php +++ b/demos/types.php @@ -4,8 +4,7 @@ use Demo\Models\TypesTest; use Pyther\Json\Exceptions\JsonException; use Pyther\Json\Json; -use Pyther\Json\JsonDeserializeSettings; -use Pyther\Json\JsonSerializeSettings; +use Pyther\Json\JsonSettings; use Pyther\Json\NamingPolicies\CamelToPascalNamingPolicy; use Pyther\Json\Types\EnumFormat; @@ -22,7 +21,7 @@ $obj = deserializeTest(file_get_contents("data/types.json")); echo "Object:\n"; var_dump($obj); - + // serialize $json = serializeTest($obj); echo "\n\nJSON:\n"; @@ -35,14 +34,16 @@ } function serializeTest($object): string { - $settings = new JsonSerializeSettings(); + $settings = new JsonSettings(); $settings->setEnumFormat(EnumFormat::Name); $settings->setNamingPolicy(new CamelToPascalNamingPolicy()); + $settings->setSkipNull(true); + $settings->setSkipEmptyArray(true); return Json::serialize($object, $settings); } function deserializeTest(string $json): TypesTest { - $settings = new JsonDeserializeSettings(); + $settings = new JsonSettings(); $settings->setNamingPolicy(new CamelToPascalNamingPolicy()); return Json::deserialize($json, TypesTest::class, $settings); } \ No newline at end of file diff --git a/readme.md b/readme.md index 3c27a12..08edb20 100644 --- a/readme.md +++ b/readme.md @@ -1,20 +1,28 @@ # Pyther.Json -A lightweight JSON (de)serializer between json and a data model with the following features: +A lightweight (de)serializer between json strings and data models with the following features: - support for nested arrays and objects - pre defined or custom naming policies - support for basic or backed enumerations. - meta/attribute support for + - property renaming - property exclusion - - (array) type + - (array) data type - datetime format - enum format +- several settings like + - include protected properties + - skip null values + - skip empty arrays + - enum format + - and more ... - takes documentation "@var" hints into account - no external dependencies +- straightforward to use Requirements: -- PHP >= 8.1 +- PHP 8.1+ ## Examples: diff --git a/src/BaseExecuter.php b/src/BaseExecuter.php index 6049905..03832bb 100644 --- a/src/BaseExecuter.php +++ b/src/BaseExecuter.php @@ -12,7 +12,7 @@ abstract class BaseExecuter function __construct(?JsonSettings $settings = null) { - $this->settings = $settings; + $this->settings = $settings ?? new JsonSettings(); } /** diff --git a/src/JsonDeserializeSettings.php b/src/JsonDeserializeSettings.php deleted file mode 100644 index be9ff01..0000000 --- a/src/JsonDeserializeSettings.php +++ /dev/null @@ -1,10 +0,0 @@ -flags = $value ? $this->flags | self::USE_PRETTY_PRINT : $this->flags & ~self::USE_PRETTY_PRINT; - return $this; - } - - public function getPrettyPrint(): bool { - return ($this->flags & self::USE_PRETTY_PRINT) != 0; - } - - /** - * Enable or disable to serialize DateTime as string (enabled by default). - * @param boolean $value - * @return static - */ - public function setDateTimeAsString(bool $value = true): static { - $this->flags = $value ? $this->flags | self::DATETIME_AS_STRING : $this->flags & ~self::DATETIME_AS_STRING; - return $this; - } - - public function getDateTimeAsString(): bool { - return ($this->flags & self::DATETIME_AS_STRING) != 0; - } - - /** - * Defines the default serialization format for enumerations (EnumFormat::Value by default). - * @param EnumFormat $format - * @return static - */ - public function setEnumFormat(EnumFormat $format): static { - $this->enumFormat = $format; - return $this; - } - - public function getEnumFormat(): EnumFormat { - return $this->enumFormat; - } -} \ No newline at end of file diff --git a/src/JsonSerializer.php b/src/JsonSerializer.php index c830623..f62e568 100644 --- a/src/JsonSerializer.php +++ b/src/JsonSerializer.php @@ -6,16 +6,8 @@ use Pyther\Json\Attributes\JsonEnum; use Pyther\Json\Types\EnumFormat; -/** - * @property JsonSerializeSettings $settings - */ class JsonSerializer extends BaseExecuter { - function __construct(?JsonSerializeSettings $settings = null) - { - parent::__construct($settings ?? new JsonSerializeSettings()); - } - /** * Serialize an object or array the the encoded json version. * @@ -72,12 +64,15 @@ private function serializeObject(object $object): ?array $typeInfo = new TypeInfo($prop); $value = $this->getValue($object, $prop); + + if ($value === null && $this->settings->getSkipNull()) continue; if ($value === null) { $data[$jsonName] = null; } // a) special case: arrays else if ($typeInfo->isArray) { + if (empty($value) && $this->settings->getSkipEmptyArrays()) continue; $data[$jsonName] = $this->serializeArray($value); } // b) special case: DateTime diff --git a/src/JsonSettings.php b/src/JsonSettings.php index 04d5389..d258e6c 100644 --- a/src/JsonSettings.php +++ b/src/JsonSettings.php @@ -2,18 +2,26 @@ namespace Pyther\Json; use Pyther\Json\NamingPolicies\BaseNamingPolicy; +use Pyther\Json\Types\EnumFormat; /** * Json settings for serialization and deserialization. */ -abstract class JsonSettings +class JsonSettings { // flags private const INCLUDE_PROTECTED_FLAG = 1; - - private int $flags = 0; + // on serialization only + private const USE_PRETTY_PRINT = 2; + private const DATETIME_AS_STRING = 4; + private const SKIP_NULL = 8; + private const SKIP_EMPTY_ARRAY = 16; + + private int $flags = self::USE_PRETTY_PRINT | self::DATETIME_AS_STRING; private ?BaseNamingPolicy $namingPolicy = null; - private string $dataTimeFormat = "Y-m-d\TH:i:s+"; + private string $dataTimeFormat = \DateTime::W3C; + // on serialization only + private EnumFormat $enumFormat = EnumFormat::Value; /** * Set to true, to enable handling protected properties. @@ -46,7 +54,7 @@ public function getNamingPolicy(): ?BaseNamingPolicy { /** - * Set the default date time format. + * Set the default date time format (\DateTime::W3C by default). * This can be overwriten per Property using "#[JsonDateTime(...)]" * * @param string $value @@ -61,4 +69,77 @@ public function getDateTimeFormat(): string { return $this->dataTimeFormat; } + #region on serialization only + + /** + * Enable or disable json indention (enabled by default). + * @param boolean $value + * @return static + */ + public function setPrettyPrint(bool $value = true): static { + $this->flags = $value ? $this->flags | self::USE_PRETTY_PRINT : $this->flags & ~self::USE_PRETTY_PRINT; + return $this; + } + + public function getPrettyPrint(): bool { + return ($this->flags & self::USE_PRETTY_PRINT) != 0; + } + + /** + * Enable or disable to serialize DateTime as string (enabled by default). + * @param boolean $value + * @return static + */ + public function setDateTimeAsString(bool $value = true): static { + $this->flags = $value ? $this->flags | self::DATETIME_AS_STRING : $this->flags & ~self::DATETIME_AS_STRING; + return $this; + } + + public function getDateTimeAsString(): bool { + return ($this->flags & self::DATETIME_AS_STRING) != 0; + } + + /** + * Defines the default serialization format for enumerations (EnumFormat::Value by default). + * @param EnumFormat $format + * @return static + */ + public function setEnumFormat(EnumFormat $format): static { + $this->enumFormat = $format; + return $this; + } + + public function getEnumFormat(): EnumFormat { + return $this->enumFormat; + } + + /** + * Define to skip null values. + * @param boolean $value + * @return static + */ + public function setSkipNull(bool $value = true): static { + $this->flags = $value ? $this->flags | self::SKIP_NULL : $this->flags & ~self::SKIP_NULL; + return $this; + } + + public function getSkipNull(): bool { + return ($this->flags & self::SKIP_NULL) != 0; + } + + /** + * Define to skip empty arrays. + * @param boolean $value + * @return static + */ + public function setSkipEmptyArray(bool $value = true): static { + $this->flags = $value ? $this->flags | self::SKIP_EMPTY_ARRAY : $this->flags & ~self::SKIP_EMPTY_ARRAY; + return $this; + } + + public function getSkipEmptyArray(): bool { + return ($this->flags & self::SKIP_EMPTY_ARRAY) != 0; + } + + #endregion } \ No newline at end of file