@@ -449,6 +449,26 @@ def get_tag(constr_id: int) -> Optional[int]:
449449 return None
450450
451451
452+ def get_constructor_id_and_fields (
453+ raw_tag : CBORTag ,
454+ ) -> typing .Tuple [int , typing .List [Any ]]:
455+ tag = raw_tag .tag
456+ if tag == 102 :
457+ if len (raw_tag .value ) != 2 :
458+ raise DeserializeException (
459+ f"Expect the length of value to be exactly 2, got { len (raw_tag .value )} instead."
460+ )
461+ return raw_tag .value [0 ], raw_tag .value [1 ]
462+ else :
463+ if 121 <= tag < 128 :
464+ constr = tag - 121
465+ elif 1280 <= tag < 1536 :
466+ constr = tag - 1280 + 7
467+ else :
468+ raise DeserializeException (f"Unexpected tag for RawPlutusData: { tag } " )
469+ return constr , raw_tag .value
470+
471+
452472def id_map (cls , skip_constructor = False ):
453473 """
454474 Constructs a unique representation of a PlutusData type definition.
@@ -579,14 +599,12 @@ def from_primitive(cls: Type[PlutusData], value: CBORTag) -> PlutusData:
579599 def hash (self ) -> DatumHash :
580600 return datum_hash (self )
581601
582- def to_json (self , ** kwargs ) -> str :
583- """Convert to a json string
584-
585- Args:
586- **kwargs: Extra key word arguments to be passed to `json.dumps()`
602+ def to_dict (self ) -> dict :
603+ """
604+ Convert to a dictionary.
587605
588606 Returns:
589- str: a JSON encoded PlutusData .
607+ str: a dict PlutusData that can be JSON encoded.
590608 """
591609
592610 def _dfs (obj ):
@@ -610,10 +628,26 @@ def _dfs(obj):
610628 "constructor" : obj .CONSTR_ID ,
611629 "fields" : [_dfs (getattr (obj , f .name )) for f in fields (obj )],
612630 }
631+ elif isinstance (obj , RawPlutusData ):
632+ return obj .to_dict ()
633+ elif isinstance (obj , RawCBOR ):
634+ return RawPlutusData .from_cbor (obj .cbor ).to_dict ()
613635 else :
614636 raise TypeError (f"Unexpected type { type (obj )} " )
615637
616- return json .dumps (_dfs (self ), ** kwargs )
638+ return _dfs (self )
639+
640+ def to_json (self , ** kwargs ) -> str :
641+ """Convert to a json string
642+
643+ Args:
644+ **kwargs: Extra key word arguments to be passed to `json.dumps()`
645+
646+ Returns:
647+ str: a JSON encoded PlutusData.
648+ """
649+
650+ return json .dumps (self .to_dict (), ** kwargs )
617651
618652 @classmethod
619653 def from_dict (cls : Type [PlutusData ], data : dict ) -> PlutusData :
@@ -640,6 +674,8 @@ def _dfs(obj):
640674 f_info .type , PlutusData
641675 ):
642676 converted_fields .append (f_info .type .from_dict (f ))
677+ elif f_info .type == Datum :
678+ converted_fields .append (RawPlutusData .from_dict (f ))
643679 elif (
644680 hasattr (f_info .type , "__origin__" )
645681 and f_info .type .__origin__ is Union
@@ -765,11 +801,104 @@ def _dfs(obj):
765801
766802 return _dfs (self .data )
767803
804+ def to_dict (self ) -> dict :
805+ """
806+ Convert to a dictionary.
807+
808+ Returns:
809+ str: a dict RawPlutusData that can be JSON encoded.
810+ """
811+
812+ def _dfs (obj ):
813+ if isinstance (obj , int ):
814+ return {"int" : obj }
815+ elif isinstance (obj , bytes ):
816+ return {"bytes" : obj .hex ()}
817+ elif isinstance (obj , ByteString ):
818+ return {"bytes" : obj .value .hex ()}
819+ elif isinstance (obj , IndefiniteList ) or isinstance (obj , list ):
820+ return {"list" : [_dfs (item ) for item in obj ]}
821+ elif isinstance (obj , dict ):
822+ return {"map" : [{"v" : _dfs (v ), "k" : _dfs (k )} for k , v in obj .items ()]}
823+ elif isinstance (obj , CBORTag ):
824+ constructor , fields = get_constructor_id_and_fields (obj )
825+ return {"constructor" : constructor , "fields" : [_dfs (f ) for f in fields ]}
826+ raise TypeError (f"Unexpected type { type (obj )} " )
827+
828+ return _dfs (RawPlutusData .to_primitive (self ))
829+
830+ def to_json (self , ** kwargs ) -> str :
831+ """Convert to a json string
832+
833+ Args:
834+ **kwargs: Extra key word arguments to be passed to `json.dumps()`
835+
836+ Returns:
837+ str: a JSON encoded RawPlutusData.
838+ """
839+
840+ return json .dumps (RawPlutusData .to_dict (self ), ** kwargs )
841+
768842 @classmethod
769843 @limit_primitive_type (CBORTag )
770844 def from_primitive (cls : Type [RawPlutusData ], value : CBORTag ) -> RawPlutusData :
771845 return cls (value )
772846
847+ @classmethod
848+ def from_dict (cls : Type [RawPlutusData ], data : dict ) -> RawPlutusData :
849+ """Convert a dictionary to RawPlutusData
850+
851+ Args:
852+ data (dict): A dictionary.
853+
854+ Returns:
855+ RawPlutusData: Restored RawPlutusData.
856+ """
857+
858+ def _dfs (obj ):
859+ if isinstance (obj , dict ):
860+ if "constructor" in obj :
861+ converted_fields = []
862+ for f in obj ["fields" ]:
863+ converted_fields .append (_dfs (f ))
864+ tag = get_tag (obj ["constructor" ])
865+ if tag is None :
866+ return CBORTag (
867+ 102 , [obj ["constructor" ], IndefiniteList (converted_fields )]
868+ )
869+ else :
870+ return CBORTag (tag , converted_fields )
871+ elif "map" in obj :
872+ return {_dfs (pair ["k" ]): _dfs (pair ["v" ]) for pair in obj ["map" ]}
873+ elif "int" in obj :
874+ return obj ["int" ]
875+ elif "bytes" in obj :
876+ if len (obj ["bytes" ]) > 64 :
877+ return ByteString (bytes .fromhex (obj ["bytes" ]))
878+ else :
879+ return bytes .fromhex (obj ["bytes" ])
880+ elif "list" in obj :
881+ return IndefiniteList ([_dfs (item ) for item in obj ["list" ]])
882+ else :
883+ raise DeserializeException (f"Unexpected data structure: { obj } " )
884+ else :
885+ raise TypeError (f"Unexpected data type: { type (obj )} " )
886+
887+ return cls (_dfs (data ))
888+
889+ @classmethod
890+ def from_json (cls : Type [RawPlutusData ], data : str ) -> RawPlutusData :
891+ """Restore a json encoded string to a RawPlutusData.
892+
893+ Args:
894+ data (str): An encoded json string.
895+
896+ Returns:
897+ RawPlutusData: The restored RawPlutusData.
898+ """
899+ obj = json .loads (data )
900+ return cls .from_dict (obj )
901+
773902 def __deepcopy__ (self , memo ):
774903 return self .__class__ .from_cbor (self .to_cbor_hex ())
775904
0 commit comments