title: Serialization sidebar_position: 30 id: serialization license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

This page demonstrates cross-language serialization patterns with examples in all supported languages. Data serialized in one language can be deserialized in any other supported language.

Serialize Built-in Types

Common types can be serialized automatically without registration: primitive numeric types, string, binary, array, list, map, and more.

Java

import org.apache.fory.*;
import org.apache.fory.config.*;

import java.util.*;

public class Example1 {
  public static void main(String[] args) {
    Fory fory = Fory.builder().withLanguage(Language.XLANG).build();
    List<Object> list = ofArrayList(true, false, "str", -1.1, 1, new int[100], new double[20]);
    byte[] bytes = fory.serialize(list);
    // bytes can be deserialized by other languages
    fory.deserialize(bytes);
    Map<Object, Object> map = new HashMap<>();
    map.put("k1", "v1");
    map.put("k2", list);
    map.put("k3", -1);
    bytes = fory.serialize(map);
    // bytes can be deserialized by other languages
    fory.deserialize(bytes);
  }
}

Python

import pyfory
import numpy as np

fory = pyfory.Fory()
object_list = [True, False, "str", -1.1, 1,
               np.full(100, 0, dtype=np.int32), np.full(20, 0.0, dtype=np.double)]
data = fory.serialize(object_list)
# bytes can be deserialized by other languages
new_list = fory.deserialize(data)
object_map = {"k1": "v1", "k2": object_list, "k3": -1}
data = fory.serialize(object_map)
# bytes can be deserialized by other languages
new_map = fory.deserialize(data)
print(new_map)

Go

package main

import forygo "github.com/apache/fory/go/fory"
import "fmt"

func main() {
  list := []any{true, false, "str", -1.1, 1, make([]int32, 10), make([]float64, 20)}
  fory := forygo.NewFory()
  bytes, err := fory.Marshal(list)
  if err != nil {
    panic(err)
  }
  var newValue any
  // bytes can be deserialized by other languages
  if err := fory.Unmarshal(bytes, &newValue); err != nil {
    panic(err)
  }
  fmt.Println(newValue)
  dict := map[string]any{
    "k1": "v1",
    "k2": list,
    "k3": -1,
  }
  bytes, err = fory.Marshal(dict)
  if err != nil {
    panic(err)
  }
  // bytes can be deserialized by other languages
  if err := fory.Unmarshal(bytes, &newValue); err != nil {
    panic(err)
  }
  fmt.Println(newValue)
}

JavaScript

import Fory from "@apache-fory/fory";

/**
 * @apache-fory/hps use v8's fast-calls-api that can be called directly by jit,
 * ensure that the version of Node is 20 or above.
 * Experimental feature, installation success cannot be guaranteed at this moment.
 * If you are unable to install the module, replace it with `const hps = null;`
 **/
import hps from "@apache-fory/hps";

const fory = new Fory({ hps });
const input = fory.serialize("hello fory");
const result = fory.deserialize(input);
console.log(result);

Rust

use fory::{from_buffer, to_buffer, Fory};
use std::collections::HashMap;

fn run() {
    let bin: Vec<u8> = to_buffer(&"hello".to_string());
    let obj: String = from_buffer(&bin).expect("should success");
    assert_eq!("hello".to_string(), obj);
}

Serialize Custom Types

User-defined types must be registered using the register API to establish the mapping relationship between types in different languages. Use consistent type names across all languages.

Java

import org.apache.fory.*;
import org.apache.fory.config.*;
import java.util.*;

public class Example2 {
  public static class SomeClass1 {
    Object f1;
    Map<Byte, Integer> f2;
  }

  public static class SomeClass2 {
    Object f1;
    String f2;
    List<Object> f3;
    Map<Byte, Integer> f4;
    Byte f5;
    Short f6;
    Integer f7;
    Long f8;
    Float f9;
    Double f10;
    short[] f11;
    List<Short> f12;
  }

  public static Object createObject() {
    SomeClass1 obj1 = new SomeClass1();
    obj1.f1 = true;
    obj1.f2 = ofHashMap((byte) -1, 2);
    SomeClass2 obj = new SomeClass2();
    obj.f1 = obj1;
    obj.f2 = "abc";
    obj.f3 = ofArrayList("abc", "abc");
    obj.f4 = ofHashMap((byte) 1, 2);
    obj.f5 = Byte.MAX_VALUE;
    obj.f6 = Short.MAX_VALUE;
    obj.f7 = Integer.MAX_VALUE;
    obj.f8 = Long.MAX_VALUE;
    obj.f9 = 1.0f / 2;
    obj.f10 = 1 / 3.0;
    obj.f11 = new short[]{(short) 1, (short) 2};
    obj.f12 = ofArrayList((short) -1, (short) 4);
    return obj;
  }

  // mvn exec:java -Dexec.mainClass="org.apache.fory.examples.Example2"
  public static void main(String[] args) {
    Fory fory = Fory.builder().withLanguage(Language.XLANG).build();
    fory.register(SomeClass1.class, "example.SomeClass1");
    fory.register(SomeClass2.class, "example.SomeClass2");
    byte[] bytes = fory.serialize(createObject());
    // bytes can be deserialized by other languages
    System.out.println(fory.deserialize(bytes));
  }
}

Python

from dataclasses import dataclass
from typing import List, Dict, Any
import pyfory, array


@dataclass
class SomeClass1:
    f1: Any
    f2: Dict[pyfory.Int8Type, pyfory.Int32Type]


@dataclass
class SomeClass2:
    f1: Any = None
    f2: str = None
    f3: List[str] = None
    f4: Dict[pyfory.Int8Type, pyfory.Int32Type] = None
    f5: pyfory.Int8Type = None
    f6: pyfory.Int16Type = None
    f7: pyfory.Int32Type = None
    # int type will be taken as `pyfory.Int64Type`.
    # use `pyfory.Int32Type` for type hint if peer uses more narrow type.
    f8: int = None
    f9: pyfory.Float32Type = None
    # float type will be taken as `pyfory.Float64Type`
    f10: float = None
    f11: pyfory.Int16ArrayType = None
    f12: List[pyfory.Int16Type] = None


if __name__ == "__main__":
    f = pyfory.Fory()
    f.register_type(SomeClass1, typename="example.SomeClass1")
    f.register_type(SomeClass2, typename="example.SomeClass2")
    obj1 = SomeClass1(f1=True, f2={-1: 2})
    obj = SomeClass2(
        f1=obj1,
        f2="abc",
        f3=["abc", "abc"],
        f4={1: 2},
        f5=2 ** 7 - 1,
        f6=2 ** 15 - 1,
        f7=2 ** 31 - 1,
        f8=2 ** 63 - 1,
        f9=1.0 / 2,
        f10=1 / 3.0,
        f11=array.array("h", [1, 2]),
        f12=[-1, 4],
    )
    data = f.serialize(obj)
    # bytes can be deserialized by other languages
    print(f.deserialize(data))

Go

package main

import forygo "github.com/apache/fory/go/fory"
import "fmt"

func main() {
  type SomeClass1 struct {
    F1  any
    F2  string
    F3  []any
    F4  map[int8]int32
    F5  int8
    F6  int16
    F7  int32
    F8  int64
    F9  float32
    F10 float64
    F11 []int16
    F12 fory.Int16Slice
  }

  type SomeClass2 struct {
    F1 any
    F2 map[int8]int32
  }
  fory := forygo.NewFory()
  if err := fory.RegisterTagType("example.SomeClass1", SomeClass1{}); err != nil {
    panic(err)
  }
  if err := fory.RegisterTagType("example.SomeClass2", SomeClass2{}); err != nil {
    panic(err)
  }
  obj1 := &SomeClass1{}
  obj1.F1 = true
  obj1.F2 = map[int8]int32{-1: 2}
  obj := &SomeClass1{}
  obj.F1 = obj1
  obj.F2 = "abc"
  obj.F3 = []any{"abc", "abc"}
  f4 := map[int8]int32{1: 2}
  obj.F4 = f4
  obj.F5 = fory.MaxInt8
  obj.F6 = fory.MaxInt16
  obj.F7 = fory.MaxInt32
  obj.F8 = fory.MaxInt64
  obj.F9 = 1.0 / 2
  obj.F10 = 1 / 3.0
  obj.F11 = []int16{1, 2}
  obj.F12 = []int16{-1, 4}
  bytes, err := fory.Marshal(obj);
  if err != nil {
    panic(err)
  }
  var newValue any
  // bytes can be deserialized by other languages
  if err := fory.Unmarshal(bytes, &newValue); err != nil {
    panic(err)
  }
  fmt.Println(newValue)
}

JavaScript

import Fory, { Type, InternalSerializerType } from "@apache-fory/fory";

/**
 * @apache-fory/hps use v8's fast-calls-api that can be called directly by jit,
 * ensure that the version of Node is 20 or above.
 * Experimental feature, installation success cannot be guaranteed at this moment.
 * If you are unable to install the module, replace it with `const hps = null;`
 **/
import hps from "@apache-fory/hps";

// Describe data structures using JSON schema
const description = Type.object("example.foo", {
  foo: Type.string(),
});
const fory = new Fory({ hps });
const { serialize, deserialize } = fory.registerSerializer(description);
const input = serialize({ foo: "hello fory" });
const result = deserialize(input);
console.log(result);

Rust

use chrono::{NaiveDate, NaiveDateTime};
use fory::{from_buffer, to_buffer, Fory};
use std::collections::HashMap;

#[test]
fn complex_struct() {
    #[derive(Fory, Debug, PartialEq)]
    #[tag("example.foo2")]
    struct Animal {
        category: String,
    }

    #[derive(Fory, Debug, PartialEq)]
    #[tag("example.foo")]
    struct Person {
        c1: Vec<u8>,  // binary
        c2: Vec<i16>, // primitive array
        animal: Vec<Animal>,
        c3: Vec<Vec<u8>>,
        name: String,
        c4: HashMap<String, String>,
        age: u16,
        op: Option<String>,
        op2: Option<String>,
        date: NaiveDate,
        time: NaiveDateTime,
        c5: f32,
        c6: f64,
    }
    let person: Person = Person {
        c1: vec![1, 2, 3],
        c2: vec![5, 6, 7],
        c3: vec![vec![1, 2], vec![1, 3]],
        animal: vec![Animal {
            category: "Dog".to_string(),
        }],
        c4: HashMap::from([
            ("hello1".to_string(), "hello2".to_string()),
            ("hello2".to_string(), "hello3".to_string()),
        ]),
        age: 12,
        name: "helo".to_string(),
        op: Some("option".to_string()),
        op2: None,
        date: NaiveDate::from_ymd_opt(2025, 12, 12).unwrap(),
        time: NaiveDateTime::from_timestamp_opt(1689912359, 0).unwrap(),
        c5: 2.0,
        c6: 4.0,
    };

    let bin: Vec<u8> = to_buffer(&person);
    let obj: Person = from_buffer(&bin).expect("should success");
    assert_eq!(person, obj);
}

Serialize Shared and Circular References

Shared references and circular references can be serialized automatically with no duplicate data or recursion errors. Enable reference tracking to use this feature.

Java

import org.apache.fory.*;
import org.apache.fory.config.*;
import java.util.*;

public class ReferenceExample {
  public static class SomeClass {
    SomeClass f1;
    Map<String, String> f2;
    Map<String, String> f3;
  }

  public static Object createObject() {
    SomeClass obj = new SomeClass();
    obj.f1 = obj;
    obj.f2 = ofHashMap("k1", "v1", "k2", "v2");
    obj.f3 = obj.f2;
    return obj;
  }

  // mvn exec:java -Dexec.mainClass="org.apache.fory.examples.ReferenceExample"
  public static void main(String[] args) {
    Fory fory = Fory.builder().withLanguage(Language.XLANG)
      .withRefTracking(true).build();
    fory.register(SomeClass.class, "example.SomeClass");
    byte[] bytes = fory.serialize(createObject());
    // bytes can be deserialized by other languages
    System.out.println(fory.deserialize(bytes));
  }
}

Python

from typing import Dict
import pyfory

class SomeClass:
    f1: "SomeClass"
    f2: Dict[str, str]
    f3: Dict[str, str]

fory = pyfory.Fory(ref_tracking=True)
fory.register_type(SomeClass, typename="example.SomeClass")
obj = SomeClass()
obj.f2 = {"k1": "v1", "k2": "v2"}
obj.f1, obj.f3 = obj, obj.f2
data = fory.serialize(obj)
# bytes can be deserialized by other languages
print(fory.deserialize(data))

Go

package main

import forygo "github.com/apache/fory/go/fory"
import "fmt"

func main() {
  type SomeClass struct {
    F1 *SomeClass
    F2 map[string]string
    F3 map[string]string
  }
  fory := forygo.NewFory(true)
  if err := fory.Register(SomeClass{}, 65); err != nil {
    panic(err)
  }
  value := &SomeClass{F2: map[string]string{"k1": "v1", "k2": "v2"}}
  value.F3 = value.F2
  value.F1 = value
  bytes, err := fory.Marshal(value)
  if err != nil {
    panic(err)
  }
  var newValue any
  // bytes can be deserialized by other languages
  if err := fory.Unmarshal(bytes, &newValue); err != nil {
    panic(err)
  }
  fmt.Println(newValue)
}

JavaScript

import Fory, { Type } from "@apache-fory/fory";
/**
 * @apache-fory/hps use v8's fast-calls-api that can be called directly by jit,
 * ensure that the version of Node is 20 or above.
 * Experimental feature, installation success cannot be guaranteed at this moment.
 * If you are unable to install the module, replace it with `const hps = null;`
 **/
import hps from "@apache-fory/hps";

const description = Type.object("example.foo", {
  foo: Type.string(),
  bar: Type.object("example.foo"),
});

const fory = new Fory({ hps });
const { serialize, deserialize } = fory.registerSerializer(description);
const data: any = {
  foo: "hello fory",
};
data.bar = data;
const input = serialize(data);
const result = deserialize(input);
console.log(result.bar.foo === result.foo);

Rust

Circular references cannot be implemented in Rust due to ownership restrictions.

See Also