Blog

Technical thoughts, troubleshooting, and development notes.

Blog/UE Development

Blueprint generic struct serialization: memory layout versus persistence

Reflected struct arguments, JSON parsing and safe persistence boundaries.

Back
This page's table of contents

UE: Data Serialization and Byte Conversion in Blueprints

Introduction

Network communication, data persistence, and interaction with external programs are very common requirements in UE projects. Whether sending structured data over UDP/TCP or serializing game state into binary storage, one core operation is unavoidable: converting data into a byte array.

However, this is not straightforward in Blueprints:

  • Blueprints have no native byte-conversion nodes; int32, float, and double cannot be converted directly to TArray<uint8>
  • Struct serialization is even more difficult; CustomThunk presents too high a barrier for Blueprint-only users
  • UE’s built-in FJsonObjectConverter suffers serious floating-point precision loss during serialization
  • Encoding formats do not match when exchanging data with external Python, C#, or C++ programs

The SimpleByteConversion plugin was created to solve these problems.

It provides a complete set of Blueprint nodes supporting bidirectional conversion between basic types or arbitrary structs and byte arrays/JSON strings, ready to use out of the box.


1. Core Features

1.1 Basic Type Conversion

The plugin covers all commonly used basic types, providing both ToBytes and FromBytes conversions for each:

TypeTo bytesRestore from bytes
int32IntToBytesBytesToInt
int64Int64ToBytesBytesToInt64
floatFloatToBytesBytesToFloat
doubleDoubleToBytesBytesToDouble
boolBoolToBytesBytesToBool
FStringStringToBytesBytesToString

All nodes are BlueprintPure and can be used directly as pure functions in Blueprints without execution pins.

How it works:

// 以 int32 为例,直接内存拷贝,零开销
void ByteConverter::IntToBytes(int32 Value, TArray<uint8>& OutBytes)
{
    OutBytes.SetNumUninitialized(sizeof(int32));
    FMemory::Memcpy(OutBytes.GetData(), &Value, sizeof(int32));
}

All basic types use direct memory copying through FMemory::Memcpy for optimal performance. String conversion uses UTF-8 encoding and automatically handles the Null Terminator to ensure cross-platform compatibility.

1.2 Struct Serialization

This is the plugin’s most powerful feature—bidirectional conversion between any Blueprint struct and byte arrays:

  • StructToBytes / BytesToStruct — Binary serialization
  • StructToJsonString / JsonStringToStruct — JSON string serialization

Key technology: CustomThunk

Blueprints cannot directly pass generic structs. The plugin implements generic struct parameters through CustomThunk + CustomStructureParam:

UFUNCTION(BlueprintPure, CustomThunk,
    meta = (CustomStructureParam = "Struct", DisplayName = "Struct To Bytes"),
    Category = "SimpleByteConversion|Struct")
static void StructToBytes(const int32& Struct, TArray<uint8>& Bytes);
DECLARE_FUNCTION(execStructToBytes);

This means you can drag any custom struct directly into a Blueprint, and the plugin automatically identifies its type and serializes it.

1.3 High-Precision JSON Conversion

UE’s default JSON serialization has two problems:

  1. Field names contain internal engine suffixes (such as MyField_5_A3B2C1D4)
  2. Floating-point precision loss (float retains only 6 significant digits)

The plugin solves both problems with a custom FExactPrintPolicy and field-name cleanup:

// 高精度输出:float 保留9位,double 保留17位
template <typename CharT>
struct FExactPrintPolicy : TCondensedJsonPrintPolicy<CharT>
{
    static void WriteFloat(FArchive* Stream, float V)
    {
        FString S = FString::Printf(TEXT("%.9f"), V);
        TCondensedJsonPrintPolicy<CharT>::WriteString(Stream, S);
    }
    static void WriteDouble(FArchive* Stream, double V)
    {
        FString S = FString::Printf(TEXT("%.17g"), V);
        TCondensedJsonPrintPolicy<CharT>::WriteString(Stream, S);
    }
};

The resulting JSON has clean field names and full numeric precision, and can be parsed directly by external programs such as Python and C#.


2. Use Cases

Scenario 1: UDP/TCP Network Communication

When sending data with SimpleUDP or SimpleTCPClient, you need to encode structured data as a byte array:

1. 定义一个结构体(如 FPlayerState:位置、血量、状态)
2. 使用 StructToBytes 将结构体转为 TArray<uint8>
3. 通过 UDP/TCP 发送字节数组
4. 接收端使用 BytesToStruct 还原为结构体

Scenario 2: Exchanging Data with Python Programs

Use JSON for cross-language data exchange:

1. UE 端:StructToJsonString 将游戏数据转为 JSON 字符串
2. 通过 TCP/WebSocket 发送给 Python 程序
3. Python 端:json.loads() 直接解析
4. 反向同理:Python json.dumps() → UE JsonStringToStruct

Scenario 3: Data Persistence

Serialize game saves into binary files:

1. 将存档结构体通过 StructToBytes 转为字节数组
2. 使用 FFileHelper::SaveArrayToFile 写入磁盘
3. 读取时 FFileHelper::LoadFileToArray 加载字节数组
4. BytesToStruct 还原为存档结构体

3. Quick Start

Step 1: Install the Plugin

Copy the plugin folder into your project’s Plugins directory, then restart the editor.

Step 2: Use It in Blueprints

All nodes are in the SimpleByteConversion category:

  • Basic types: Search for IntToBytes, FloatToBytes, etc.
  • Structs: Search for StructToBytes, StructToJsonString, etc.

Step 3: Use It in C++

If you need it in C++, call the ByteConverter utility class directly:

#include "ByteConverter.h"

// 基础类型
TArray<uint8> Bytes;
ByteConverter::IntToBytes(42, Bytes);

// 结构体
FMyStruct Data;
ByteConverter::StructToBytes(&Data, FMyStruct::StaticStruct(), Bytes);

// JSON
FString JsonStr;
ByteConverter::StructToJsonString(&Data, FMyStruct::StaticStruct(), JsonStr);

4. Getting the Plugin

Supported versions: Unreal Engine 5.2+

How to get it: Search for SimpleByteConversion in the Fab store

Feel free to discuss any questions in the comments.


Summary

SimpleByteConversion solves the core pain points of data serialization in UE Blueprints:

  • Basic types: Bidirectional conversion between int32/int64/float/double/bool/FString and byte arrays
  • Structs: Binary and JSON serialization for arbitrary Blueprint structs
  • High precision: Custom output policy with no loss of floating-point precision
  • Cross-language: Clean JSON output for convenient interaction with external programs

Whether you are working on network communication, data persistence, or cross-language data exchange, this plugin can save you a great deal of low-level coding.


Thanks for reading. Feel free to like, follow, bookmark, and discuss questions in the comments.
If this article helped you, click here to donate and support the author.