1111
1212namespace Temporal \DataConverter ;
1313
14+ use Doctrine \Common \Annotations \Reader ;
15+ use Spiral \Attributes \AnnotationReader ;
16+ use Spiral \Attributes \AttributeReader ;
17+ use Spiral \Attributes \Composite \SelectiveReader ;
18+ use Spiral \Attributes \ReaderInterface ;
1419use Temporal \Api \Common \V1 \Payload ;
1520use Temporal \Exception \DataConverterException ;
21+ use Temporal \Internal \Marshaller \Mapper \AttributeMapperFactory ;
22+ use Temporal \Internal \Marshaller \Marshaller ;
23+ use Temporal \Internal \Marshaller \MarshallerInterface ;
1624
25+ /**
26+ * Json converter with the ability to serialize/unserialize DTO objects using JSON.
27+ */
1728class JsonConverter extends Converter
1829{
30+ /**
31+ * @var int
32+ */
33+ public const JSON_FLAGS = \JSON_THROW_ON_ERROR | \JSON_PRESERVE_ZERO_FRACTION ;
34+
35+ /**
36+ * @var MarshallerInterface
37+ */
38+ private MarshallerInterface $ marshaller ;
39+
40+ /**
41+ * @param MarshallerInterface|null $marshaller
42+ */
43+ public function __construct (MarshallerInterface $ marshaller = null )
44+ {
45+ $ this ->marshaller = $ marshaller ?? self ::createDefaultMarshaller ();
46+ }
47+
1948 /**
2049 * @return string
2150 */
@@ -27,12 +56,19 @@ public function getEncodingType(): string
2756 /**
2857 * @param mixed $value
2958 * @return Payload|null
30- * @throws \JsonException
59+ * @throws DataConverterException
3160 */
3261 public function toPayload ($ value ): ?Payload
3362 {
63+ if (\is_object ($ value )) {
64+ $ value = $ value instanceof \stdClass
65+ ? $ value
66+ : $ this ->marshaller ->marshal ($ value )
67+ ;
68+ }
69+
3470 try {
35- return self :: create (\json_encode ($ value , \ JSON_THROW_ON_ERROR ));
71+ return $ this -> create (\json_encode ($ value , self :: JSON_FLAGS ));
3672 } catch (\Throwable $ e ) {
3773 throw new DataConverterException ($ e ->getMessage (), $ e ->getCode (), $ e );
3874 }
@@ -42,13 +78,142 @@ public function toPayload($value): ?Payload
4278 * @param Payload $payload
4379 * @param Type $type
4480 * @return mixed|void
81+ * @throws DataConverterException
4582 */
4683 public function fromPayload (Payload $ payload , Type $ type )
4784 {
4885 try {
49- return \json_decode ($ payload ->getData (), true , 512 , \ JSON_THROW_ON_ERROR );
86+ $ data = \json_decode ($ payload ->getData (), false , 512 , self :: JSON_FLAGS );
5087 } catch (\Throwable $ e ) {
5188 throw new DataConverterException ($ e ->getMessage (), $ e ->getCode (), $ e );
5289 }
90+
91+ switch ($ type ->getName ()) {
92+ case Type::TYPE_ANY :
93+ return $ data ;
94+
95+ case Type::TYPE_STRING :
96+ if (! \is_string ($ data )) {
97+ throw $ this ->errorInvalidType ($ type , $ data );
98+ }
99+
100+ return $ data ;
101+
102+ case Type::TYPE_FLOAT :
103+ if (! \is_float ($ data )) {
104+ throw $ this ->errorInvalidType ($ type , $ data );
105+ }
106+
107+ return $ data ;
108+
109+ case Type::TYPE_INT :
110+ if (! \is_int ($ data )) {
111+ throw $ this ->errorInvalidType ($ type , $ data );
112+ }
113+
114+ return $ data ;
115+
116+ case Type::TYPE_BOOL :
117+ if (! \is_bool ($ data )) {
118+ throw $ this ->errorInvalidType ($ type , $ data );
119+ }
120+
121+ return $ data ;
122+
123+ case Type::TYPE_ARRAY :
124+ if (! \is_array ($ data )) {
125+ throw $ this ->errorInvalidType ($ type , $ data );
126+ }
127+
128+ return $ data ;
129+
130+ case Type::TYPE_OBJECT :
131+ if (! \is_object ($ data )) {
132+ throw $ this ->errorInvalidType ($ type , $ data );
133+ }
134+
135+ return $ data ;
136+ }
137+
138+ if ((\is_object ($ data ) || \is_array ($ data )) && $ type ->isClass ()) {
139+ try {
140+ $ instance = (new \ReflectionClass ($ type ->getName ()))
141+ ->newInstanceWithoutConstructor ()
142+ ;
143+ } catch (\ReflectionException $ e ) {
144+ throw new DataConverterException ($ e ->getMessage (), $ e ->getCode (), $ e );
145+ }
146+
147+ return $ this ->marshaller ->unmarshal ($ this ->toHashMap ($ data ), $ instance );
148+ }
149+
150+ throw $ this ->errorInvalidTypeName ($ type );
151+ }
152+
153+ /**
154+ * @param object|array $context
155+ * @return array
156+ */
157+ private function toHashMap ($ context ): array
158+ {
159+ if (\is_object ($ context )) {
160+ $ context = (array )$ context ;
161+ }
162+
163+ foreach ($ context as $ key => $ value ) {
164+ if (\is_object ($ value ) || \is_array ($ value )) {
165+ $ context [$ key ] = $ this ->toHashMap ($ value );
166+ }
167+ }
168+
169+ return $ context ;
170+ }
171+
172+ /**
173+ * @param Type $type
174+ * @return DataConverterException
175+ */
176+ private function errorInvalidTypeName (Type $ type ): DataConverterException
177+ {
178+ $ message = \vsprintf ('Type named "%s" is not a valid type name ' , [
179+ $ type ->getName ()
180+ ]);
181+
182+ return new DataConverterException ($ message );
183+ }
184+
185+ /**
186+ * @param Type $type
187+ * @param mixed $data
188+ * @return DataConverterException
189+ */
190+ private function errorInvalidType (Type $ type , $ data ): DataConverterException
191+ {
192+ $ message = \vsprintf ('The passed value of type "%s" can not be converted to required type "%s" ' , [
193+ \get_debug_type ($ data ),
194+ $ type ->getName ()
195+ ]);
196+
197+ return new DataConverterException ($ message );
198+ }
199+
200+ /**
201+ * @return MarshallerInterface
202+ */
203+ private static function createDefaultMarshaller (): MarshallerInterface
204+ {
205+ return new Marshaller (new AttributeMapperFactory (self ::createDefaultReader ()));
206+ }
207+
208+ /**
209+ * @return ReaderInterface
210+ */
211+ private static function createDefaultReader (): ReaderInterface
212+ {
213+ if (\interface_exists (Reader::class)) {
214+ return new SelectiveReader ([new AnnotationReader (), new AttributeReader ()]);
215+ }
216+
217+ return new AttributeReader ();
53218 }
54219}
0 commit comments