Polymorphism and Dynamic Types
Fory Swift supports dynamic serialization for Any, AnyObject, arbitrary
application protocol existentials, and supported heterogeneous collections.
Any and AnyObject roots use direct convenience APIs. Application protocols
and recursively composed carriers select DynamicSerializer explicitly.
Dynamic Roots
let fory = Fory()
let dynamic: Any = Int32(7)
let data = try fory.serialize(dynamic)
let decoded: Any = try fory.deserialize(data)
The equivalent explicit form is
with: DynamicSerializer<Any>.self. It is useful when composing serializers,
but is unnecessary for an Any root. AnyObject has the same direct root
APIs.
A concrete external value passed as Any may use its registered serializer.
For typed roots and fields, select a separate serializer explicitly with
with:.
Heterogeneous containers compose carrier serializers:
typealias AnyArraySerializer =
ArraySerializer<DynamicSerializer<Any>>
typealias StringAnyMapSerializer = DictionarySerializer<
String,
DynamicSerializer<Any>
>
Use DynamicSerializer<AnyHashable> for an erased dynamic dictionary key.
Use the exact heterogeneous shape for dynamic lists and maps: [Any],
[String: Any], [Int32: Any], or [AnyHashable: Any]. For example, write
["a", "b"] as [Any] before storing that heterogeneous list in Any.
Keep homogeneous lists and maps in their ordinary concrete types.
Application Protocols
Register each concrete target that may appear. No Fory-specific marker protocol is required:
protocol Animal {
var name: String { get }
}
@ForyStruct
struct Dog: Animal {
var name: String = ""
}
@ForyStruct(target: ThirdParty.Cat.self)
struct CatSerializer {
var name: String
}
let fory = Fory()
try fory.register(Dog.self, id: 100)
try fory.register(CatSerializer.self, id: 101)
let input: any Animal = Dog(name: "Rex")
let data = try fory.serialize(
input,
with: DynamicSerializer<any Animal>.self
)
let output = try fory.deserialize(
data,
with: DynamicSerializer<any Animal>.self
)
Every concrete target must conform to the requested application protocol. A target that is unregistered or does not conform fails deserialization.
Protocol Fields
Application protocol fields are dynamic:
@ForyStruct
struct Zoo {
var featured: any Animal
var animals: [any Animal]
}
Register Zoo and every concrete target that may appear.
Use an optional when the field needs a nil default:
@ForyStruct
struct OptionalZoo {
var featured: (any Animal)? = nil
}
Protocol Root Carriers
Use DynamicSerializer<T> as the child of a root carrier:
typealias AnimalArraySerializer =
ArraySerializer<DynamicSerializer<any Animal>>
let data = try fory.serialize(
animals,
with: AnimalArraySerializer.self
)
let decoded = try fory.deserialize(
data,
with: AnimalArraySerializer.self
)
Optional and map roots compose the same way:
typealias FeaturedSerializer =
OptionalSerializer<DynamicSerializer<any Animal>>
typealias AnimalMapSerializer = DictionarySerializer<
String,
DynamicSerializer<any Animal>
>
Swift's normal Hashable rules still apply to set elements and dictionary
keys. Use AnyHashable for erased dynamic keys.
Explicitly Polymorphic Class Nodes
Select DynamicSerializer<Base> when a class-typed field must preserve its
concrete subclass:
@ForyField(with: DynamicSerializer<AnimalBase>.self)
var animal: AnimalBase
Register every concrete subclass that may appear.
Dynamic Any Fields
@ForyStruct
struct DynamicHolder {
var value: Any = ForyAnyNullValue()
var list: [Any] = []
var byName: [String: Any] = [:]
var byId: [Int32: Any] = [:]
var byDynamicKey: [AnyHashable: Any] = [:]
}
Concrete Type Registration Still Applies
If dynamic values contain user-defined types, register those concrete types.
@ForyStruct
struct Address {
var street: String = ""
var zip: Int32 = 0
}
let fory = Fory()
try fory.register(Address.self, id: 100)
Null Semantics
Anynull representation:ForyAnyNullValueAnyObjectnull representation:NSNullAnyHashabledynamic null-key representation:AnyHashable(ForyAnyNullValue())- Optional dynamic values map to the corresponding null representation on decode
AnyHashable Keys
AnyHashable keys must wrap values that are both Hashable and supported by
Fory dynamic serialization.