Serialization

The Serialization module provides type-safe JSON serialization for OpenAPI data types in RxInferServer.jl, with special focus on handling multi-dimensional arrays and probability distributions.

Supported Types

The serialization supports standard OpenAPI data types as defined in the OpenAPI specification:

  • string (including dates and files)
  • number
  • integer
  • boolean
  • array (corresponds to AbstractVector for Julia)
  • object (corresponds to AbstractDict for Julia)
  • distribution (corresponds to Distribution from RxInfer.jl)

Multi-dimensional Array Serialization

OpenAPI specification does not natively support multi-dimensional arrays. To handle this limitation while ensuring compatibility across different programming languages (which may use different array layouts), RxInferServer implements a customizable serialization strategy through two key preferences:

  1. Array representation format (RxInferServer.Serialization.MultiDimensionalArrayRepr)
  2. Array data encoding (RxInferServer.Serialization.MultiDimensionalArrayData)

Example: Matrix Serialization Challenge

Consider serializing a 2x2 matrix:

A = [1 2; 3 4]
2×2 Matrix{Int64}:
 1  2
 3  4

A naive approach might serialize this as:

encoded_A = Dict(
    "shape" => size(A),
    "data" => collect(Iterators.flatten(A))
)
Dict{String, Any} with 2 entries:
  "shape" => (2, 2)
  "data"  => [1, 3, 2, 4]

While this works fine in Julia:

decoded_A = reshape(encoded_A["data"], encoded_A["shape"]...)
2×2 Matrix{Int64}:
 1  2
 3  4

It, however, does not work in python:

>>> import numpy as np

>>> np.reshape([ 1, 3, 2, 4 ], shape = (2, 2))
array([[1, 3],
       [2, 4]])

You can see that the data is not correctly reshaped in python. Elements 2 and 3 are swapped. If we would send this data to a python SDK client, the data would be incorrect. This is due to the different array layout conventions (row-major in Python/NumPy vs column-major in Julia). RxInferServer does not assume a particular layout for multi-dimensional arrays, so it leaves it to the user to configure the serialization preferences.

Serialization Preferences

RxInferServer provides two key preferences to control how multi-dimensional arrays are serialized:

  1. Array Data Encoding (RxInferServer.Serialization.MultiDimensionalArrayData): Determines how the array data itself is transformed for serialization. For example, the ArrayOfArrays option encodes multi-dimensional arrays as nested arrays with row-major ordering to ensure cross-language compatibility.

  2. Array Representation Format (RxInferServer.Serialization.MultiDimensionalArrayRepr): Controls the structure of the serialized output, offering several options ranging from a fully-specified dictionary with type, encoding, shape, and data fields to a minimal representation with just the data itself.

These preferences can be configured when creating a RxInferServer.Serialization.JSONSerialization instance, allowing you to balance between explicit metadata and compact representation based on your specific needs. Read the Request Preferences section for more information on how to set these preferences in the request headers.

Multi-dimensional Array Representation Format

RxInferServer.Serialization.MultiDimensionalArrayRepr.DictConstant

Represents the multi-dimensional array as a dictionary with the following keys:

  • type set to "mdarray"
  • encoding set to to a selected transformation of the array, e.g. "array_of_arrays"
  • shape set to the size of the array
  • data set to the encoded array itself as defined by the transformation
julia> import RxInferServer.Serialization: MultiDimensionalArrayData, MultiDimensionalArrayRepr, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_repr = MultiDimensionalArrayRepr.Dict, mdarray_data = MultiDimensionalArrayData.ArrayOfArrays);

julia> to_json(s, [1 2; 3 4])
"{\"type\":\"mdarray\",\"encoding\":\"array_of_arrays\",\"shape\":[2,2],\"data\":[[1,2],[3,4]]}"
source
RxInferServer.Serialization.MultiDimensionalArrayRepr.DictTypeAndShapeConstant

Same as RxInferServer.Serialization.MultiDimensionalArrayRepr.Dict, but excludes the encoding key, leaving only the type, shape and data keys.

julia> import RxInferServer.Serialization: MultiDimensionalArrayData, MultiDimensionalArrayRepr, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_repr = MultiDimensionalArrayRepr.DictTypeAndShape, mdarray_data = MultiDimensionalArrayData.ArrayOfArrays);

julia> to_json(s, [1 2; 3 4])
"{\"type\":\"mdarray\",\"shape\":[2,2],\"data\":[[1,2],[3,4]]}"
source
RxInferServer.Serialization.MultiDimensionalArrayRepr.DictShapeConstant

Same as RxInferServer.Serialization.MultiDimensionalArrayRepr.Dict, but excludes the encoding and type keys, leaving only the shape and data keys.

julia> import RxInferServer.Serialization: MultiDimensionalArrayData, MultiDimensionalArrayRepr, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_repr = MultiDimensionalArrayRepr.DictShape, mdarray_data = MultiDimensionalArrayData.ArrayOfArrays);

julia> to_json(s, [1 2; 3 4])
"{\"shape\":[2,2],\"data\":[[1,2],[3,4]]}"
source
RxInferServer.Serialization.MultiDimensionalArrayRepr.DataConstant

Compact representation of the multi-dimensional array as returned from the transformation.

julia> import RxInferServer.Serialization: MultiDimensionalArrayData, MultiDimensionalArrayRepr, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_repr = MultiDimensionalArrayRepr.Data, mdarray_data = MultiDimensionalArrayData.ArrayOfArrays);

julia> to_json(s, [1 2; 3 4])
"[[1,2],[3,4]]"
source

Multi-dimensional Array Data Encoding

RxInferServer.Serialization.MultiDimensionalArrayData.ArrayOfArraysConstant

Encodes the data of multi-dimensional arrays as nested arrays of arrays with row-major ordering.

julia> import RxInferServer.Serialization: MultiDimensionalArrayData, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_data = MultiDimensionalArrayData.ArrayOfArrays);

julia> to_json(s, [1 2; 3 4])
"{\"type\":\"mdarray\",\"encoding\":\"array_of_arrays\",\"shape\":[2,2],\"data\":[[1,2],[3,4]]}"

julia> to_json(s, [1 3; 2 4])
"{\"type\":\"mdarray\",\"encoding\":\"array_of_arrays\",\"shape\":[2,2],\"data\":[[1,3],[2,4]]}"
Note

Julia uses column-major ordering for multi-dimensional arrays, but this setting explicitly uses row-major ordering.

source
RxInferServer.Serialization.MultiDimensionalArrayData.ReshapeColumnMajorConstant

Encodes the data of multi-dimensional arrays as a flattened array using column-major ordering.

julia> import RxInferServer.Serialization: MultiDimensionalArrayData, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_data = MultiDimensionalArrayData.ReshapeColumnMajor);

julia> to_json(s, [1 2; 3 4])
"{\"type\":\"mdarray\",\"encoding\":\"reshape_column_major\",\"shape\":[2,2],\"data\":[1,3,2,4]}"

julia> to_json(s, [1 3; 2 4])
"{\"type\":\"mdarray\",\"encoding\":\"reshape_column_major\",\"shape\":[2,2],\"data\":[1,2,3,4]}"
Note

Julia uses column-major ordering for multi-dimensional arrays, so this encoding preserves the natural ordering of elements in memory.

source
RxInferServer.Serialization.MultiDimensionalArrayData.ReshapeRowMajorConstant

Encodes the data of multi-dimensional arrays as a flattened array using row-major ordering.

julia> import RxInferServer.Serialization: MultiDimensionalArrayData, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_data = MultiDimensionalArrayData.ReshapeRowMajor);

julia> to_json(s, [1 2; 3 4])
"{\"type\":\"mdarray\",\"encoding\":\"reshape_row_major\",\"shape\":[2,2],\"data\":[1,2,3,4]}"

julia> to_json(s, [1 3; 2 4])
"{\"type\":\"mdarray\",\"encoding\":\"reshape_row_major\",\"shape\":[2,2],\"data\":[1,3,2,4]}"
Note

This encoding traverses the array in row-major order, which is different from Julia's native column-major storage, but is compatible with numpy.ndarray memory layout.

source
RxInferServer.Serialization.MultiDimensionalArrayData.DiagonalConstant

Encodes the data of multi-dimensional arrays as a single array containing only the diagonal elements of the parent array.

julia> import RxInferServer.Serialization: MultiDimensionalArrayData, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_data = MultiDimensionalArrayData.Diagonal);

julia> to_json(s, [1 2; 3 4])
"{\"type\":\"mdarray\",\"encoding\":\"diagonal\",\"shape\":[2,2],\"data\":[1,4]}"
source
RxInferServer.Serialization.MultiDimensionalArrayData.NoneConstant

Removes the multi-dimensional array data from the response entirely.

julia> import RxInferServer.Serialization: MultiDimensionalArrayData, JSONSerialization, to_json

julia> s = JSONSerialization(mdarray_data = MultiDimensionalArrayData.None);

julia> to_json(s, [1 2; 3 4])
"{\"type\":\"mdarray\",\"encoding\":\"none\",\"shape\":[2,2],\"data\":null}"
source

Distribution Serialization

RxInferServer provides robust serialization support for probability distributions from RxInfer.jl, enabling seamless communication between the server and clients in different programming languages. This functionality is particularly important when working with statistical models that rely on various distribution types. The serialization system offers flexible options to control how distributions are represented in JSON, allowing you to choose between detailed representations that include type information and compact formats that focus solely on the distribution parameters.

Serialization Preferences

RxInferServer provides customizable serialization for probability distributions from RxInfer.jl. Similar to multi-dimensional arrays, the serialization strategy is controlled through two key preferences:

  1. Distribution Data Encoding (RxInferServer.Serialization.DistributionsData): Determines how the distribution parameters are encoded. For example, the NamedParams option encodes parameters with their semantic names (e.g., μ for mean), while the Params option uses a simple array of values.

  2. Distribution Representation Format (RxInferServer.Serialization.DistributionsRepr): Controls the structure of the serialized output, offering several options ranging from a fully-specified dictionary with type, encoding, tag, and data fields to a minimal representation with just the data itself.

These preferences can be configured when creating a RxInferServer.Serialization.JSONSerialization instance, allowing you to balance between explicit metadata and compact representation based on your specific needs. Read the Request Preferences section for more information on how to set these preferences in the request headers.

Distribution Representation Format

RxInferServer.Serialization.DistributionsRepr.DictConstant

Represents the distribution as a dictionary with the following keys:

  • type set to the distribution type (e.g. "Distribution{Univariate, Continuous}")
  • encoding set to the selected encoding format (e.g. "named_params")
  • tag set to the specific distribution tag (e.g. "NormalMeanVariance")
  • data set to the encoded distribution parameters
julia> import RxInferServer.Serialization: DistributionsData, DistributionsRepr, JSONSerialization, to_json

julia> using RxInfer

julia> s = JSONSerialization(distributions_repr = DistributionsRepr.Dict, distributions_data = DistributionsData.NamedParams);

julia> to_json(s, NormalMeanVariance(1.0, 2.0))
"{\"encoding\":\"named_params\",\"type\":\"Distribution{Univariate, Continuous}\",\"tag\":\"NormalMeanVariance\",\"data\":{\"μ\":1.0,\"v\":2.0}}"
source
RxInferServer.Serialization.DistributionsRepr.DictTypeAndTagConstant

Same as RxInferServer.Serialization.DistributionsRepr.Dict, but excludes the encoding key, leaving only the type, tag and data keys.

julia> import RxInferServer.Serialization: DistributionsData, DistributionsRepr, JSONSerialization, to_json

julia> using RxInfer

julia> s = JSONSerialization(distributions_repr = DistributionsRepr.DictTypeAndTag, distributions_data = DistributionsData.NamedParams);

julia> to_json(s, NormalMeanVariance(1.0, 2.0))
"{\"type\":\"Distribution{Univariate, Continuous}\",\"tag\":\"NormalMeanVariance\",\"data\":{\"μ\":1.0,\"v\":2.0}}"
source
RxInferServer.Serialization.DistributionsRepr.DictTagConstant

Same as RxInferServer.Serialization.DistributionsRepr.Dict, but excludes the encoding and type keys, leaving only the tag and data keys.

julia> import RxInferServer.Serialization: DistributionsData, DistributionsRepr, JSONSerialization, to_json

julia> using RxInfer

julia> s = JSONSerialization(distributions_repr = DistributionsRepr.DictTag, distributions_data = DistributionsData.NamedParams);

julia> to_json(s, NormalMeanVariance(1.0, 2.0))
"{\"tag\":\"NormalMeanVariance\",\"data\":{\"μ\":1.0,\"v\":2.0}}"
source
RxInferServer.Serialization.DistributionsRepr.DataConstant

Compact representation of the distribution data as returned from the encoding.

julia> import RxInferServer.Serialization: DistributionsData, DistributionsRepr, JSONSerialization, to_json

julia> using RxInfer

julia> s = JSONSerialization(distributions_repr = DistributionsRepr.Data, distributions_data = DistributionsData.NamedParams);

julia> to_json(s, NormalMeanVariance(1.0, 2.0))
"{\"μ\":1.0,\"v\":2.0}"
source

Distribution Data Encoding

RxInferServer.Serialization.DistributionsData.NamedParamsConstant

Encodes the data of distributions as a dictionary with named parameters.

julia> import RxInferServer.Serialization: DistributionsData, JSONSerialization, to_json

julia> using RxInfer

julia> s = JSONSerialization(distributions_data = DistributionsData.NamedParams);

julia> to_json(s, NormalMeanVariance(1.0, 2.0))
"{\"encoding\":\"named_params\",\"type\":\"Distribution{Univariate, Continuous}\",\"tag\":\"NormalMeanVariance\",\"data\":{\"μ\":1.0,\"v\":2.0}}"
Note

This encoding preserves the semantic meaning of each parameter by using its name as a key in the dictionary.

source
RxInferServer.Serialization.DistributionsData.ParamsConstant

Encodes the data of distributions as an array of parameters in their natural order.

julia> import RxInferServer.Serialization: DistributionsData, JSONSerialization, to_json

julia> using RxInfer

julia> s = JSONSerialization(distributions_data = DistributionsData.Params);

julia> to_json(s, NormalMeanVariance(1.0, 2.0))
"{\"encoding\":\"params\",\"type\":\"Distribution{Univariate, Continuous}\",\"tag\":\"NormalMeanVariance\",\"data\":[1.0,2.0]}"
Note

This encoding is more compact but requires knowledge of the parameter order for each distribution type.

source
RxInferServer.Serialization.DistributionsData.MeanCovConstant

Always encodes distributions with mean and covariance parameters, converting from other parameterizations as needed.

julia> import RxInferServer.Serialization: DistributionsData, JSONSerialization, to_json

julia> using RxInfer

julia> s = JSONSerialization(distributions_data = DistributionsData.MeanCov);

julia> to_json(s, NormalMeanVariance(1.0, 2.0))
"{\"encoding\":\"mean_cov\",\"type\":\"Distribution{Univariate, Continuous}\",\"tag\":\"NormalMeanVariance\",\"data\":{\"mean\":1.0,\"cov\":2.0}}"

julia> to_json(s, NormalMeanPrecision(1.0, 0.5))
"{\"encoding\":\"mean_cov\",\"type\":\"Distribution{Univariate, Continuous}\",\"tag\":\"NormalMeanPrecision\",\"data\":{\"mean\":1.0,\"cov\":2.0}}"
Note

This encoding ensures consistent parameterization across different distribution types, making it easier for clients to work with the data.

source
RxInferServer.Serialization.DistributionsData.NoneConstant

Removes the distribution data from the response entirely.

julia> import RxInferServer.Serialization: DistributionsData, JSONSerialization, to_json

julia> using RxInfer

julia> s = JSONSerialization(distributions_data = DistributionsData.None);

julia> to_json(s, NormalMeanVariance(1.0, 2.0))
"{\"encoding\":\"none\",\"type\":\"Distribution{Univariate, Continuous}\",\"tag\":\"NormalMeanVariance\",\"data\":null}"
source

API Reference

RxInferServer.Serialization.from_jsonFunction
from_json(string)

Parse a JSON string into Julia data structures.

julia> import RxInferServer.Serialization: JSONSerialization, MultiDimensionalArrayRepr, MultiDimensionalArrayData, to_json, from_json

julia> s = JSONSerialization(mdarray_repr = MultiDimensionalArrayRepr.Dict, mdarray_data = MultiDimensionalArrayData.ArrayOfArrays);

julia> from_json(to_json(s, [1 2; 3 4]))
Dict{String, Any} with 4 entries:
  "shape"    => Any[2, 2]
  "encoding" => "array_of_arrays"
  "data"     => Any[Any[1, 2], Any[3, 4]]
  "type"     => "mdarray"

Note: No post-processing is performed on the deserialized value.

source