├── .gitignore ├── Config └── FilterPlugin.ini ├── JsonSerialization.uplugin ├── LICENSE ├── README.md ├── Resources ├── Icon128.png ├── ObjectToJson.PNG └── analytics_collector_object.png └── Source └── JsonSerialization ├── JsonSerialization.Build.cs ├── Private ├── JsonSerialization.cpp └── JsonSerializationBlueprintFunctionLibrary.cpp └── Public ├── JsonSerialization.h └── JsonSerializationBlueprintFunctionLibrary.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Rider C++ 5 | .idea/ 6 | Plugins/Developer/RiderLink/ 7 | 8 | # Visual Studio 2015 database file 9 | *.VC.db 10 | 11 | # Compiled Object files 12 | *.slo 13 | *.lo 14 | *.o 15 | *.obj 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Compiled Dynamic libraries 22 | *.so 23 | *.dylib 24 | *.dll 25 | 26 | # Fortran module files 27 | *.mod 28 | 29 | # Compiled Static libraries 30 | *.lai 31 | *.la 32 | *.a 33 | *.lib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | *.ipa 40 | 41 | # These project files can be generated by the engine 42 | *.xcodeproj 43 | *.xcworkspace 44 | *.sln 45 | *.suo 46 | *.opensdf 47 | *.sdf 48 | *.VC.db 49 | *.VC.opendb 50 | 51 | # Precompiled Assets 52 | SourceArt/**/*.png 53 | SourceArt/**/*.tga 54 | 55 | # Binary Files 56 | Binaries/* 57 | Plugins/*/Binaries/* 58 | 59 | # Builds 60 | Build/* 61 | 62 | # Whitelist PakBlacklist-.txt files 63 | !Build/*/ 64 | Build/*/** 65 | !Build/*/PakBlacklist*.txt 66 | 67 | # Don't ignore icon files in Build 68 | !Build/**/*.ico 69 | 70 | # Built data for maps 71 | *_BuiltData.uasset 72 | 73 | # Configuration files generated by the Editor 74 | Saved/* 75 | 76 | # Compiled source files for the engine to use 77 | Intermediate/* 78 | Plugins/*/Intermediate/* 79 | 80 | # Cache files for the editor to use 81 | DerivedDataCache/* -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 4 | ; 5 | ; Examples: 6 | ; /README.txt 7 | ; /Extras/... 8 | ; /Binaries/ThirdParty/*.dll 9 | -------------------------------------------------------------------------------- /JsonSerialization.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 2, 4 | "VersionName": "1.1", 5 | "FriendlyName": "Json Serialization", 6 | "Description": "Adds a function Object to Json that converts any UObject into Json", 7 | "Category": "Serialization", 8 | "CreatedBy": "EPICGameGuy", 9 | "CreatedByURL": "https://github.com/nicholas477", 10 | "DocsURL": "https://github.com/nicholas477/JsonSerialization", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": false, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "JsonSerialization", 20 | "Type": "Runtime", 21 | "LoadingPhase": "Default", 22 | "AdditionalDependencies": [ 23 | "StructUtils" 24 | ] 25 | } 26 | ], 27 | "Plugins": [ 28 | { 29 | "Name": "StructUtils", 30 | "Enabled": true 31 | } 32 | ] 33 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Nicholas Chalkley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Json Serialization Plugin 2 | This plugin adds support for serializing UObjects to json. It supports nested objects, arrays, structs, etc. It serializes all non-transient properties, and it does not serialize cyclical references. 3 | 4 | ## How to use 5 | 6 | It comes with one function: 7 | 8 | `TSharedPtr FJsonSerializationModule::SerializeUObjectToJson(const UObject* Object)` 9 | 10 | It's also got a blueprint function that does the same thing: 11 | 12 | ![Blueprint Object to Json function](Resources/ObjectToJson.PNG?raw=true") 13 | 14 | If you use the blueprint function, remember to enable the `Json Blueprint Utilities` plugin to be able to convert the outputted json to a string in BP. 15 | 16 | ## Example output 17 | 18 | Here's an example of the output. Here's a blueprint: 19 | 20 | ![Example blueprint](Resources/analytics_collector_object.png?raw=true") 21 | 22 | And it's json serialization: 23 | 24 | ```json 25 | { 26 | "StartTime": "2023.06.29-19.28.24", 27 | "EndTime": "2023.06.29-19.28.25", 28 | "BuildData": { 29 | "BuildVersion": "++UE5+Release-5.2-CL-25360045", 30 | "BuildConfiguration": "Development", 31 | "PlatformName": "Windows" 32 | }, 33 | "PlayerControllerData": [ 34 | { 35 | "IsLocal": true, 36 | "PawnData": [ 37 | { 38 | "ClassName": "BP_PlayerCactusCharacter_C", 39 | "PossessTime": "2023.06.29-19.28.24", 40 | "Level": "/Game/Cactus/Maps/JobInterview/JobInterview.JobInterview:PersistentLevel", 41 | "ExtraData": { 42 | "DeathData": [] 43 | } 44 | } 45 | ] 46 | } 47 | ], 48 | "IsUsingSteam": false, 49 | "IsPlayInEditorSession": true 50 | } 51 | ``` 52 | As you can see, it supports nested objects (`BP_PlayerControllerData`) and arrays and structs. 53 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicholas477/JsonSerialization/cc10425477db47dcf6bc790230aeb36bd37e578f/Resources/Icon128.png -------------------------------------------------------------------------------- /Resources/ObjectToJson.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicholas477/JsonSerialization/cc10425477db47dcf6bc790230aeb36bd37e578f/Resources/ObjectToJson.PNG -------------------------------------------------------------------------------- /Resources/analytics_collector_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicholas477/JsonSerialization/cc10425477db47dcf6bc790230aeb36bd37e578f/Resources/analytics_collector_object.png -------------------------------------------------------------------------------- /Source/JsonSerialization/JsonSerialization.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class JsonSerialization : ModuleRules 6 | { 7 | public JsonSerialization(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange( 12 | new string[] 13 | { 14 | "Core", 15 | "Json", 16 | "JsonUtilities" 17 | } 18 | ); 19 | 20 | PrivateDependencyModuleNames.AddRange( 21 | new string[] 22 | { 23 | "CoreUObject", 24 | "Engine", 25 | "Slate", 26 | "SlateCore", 27 | "StructUtils" 28 | } 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/JsonSerialization/Private/JsonSerialization.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "JsonSerialization.h" 4 | 5 | #include "JsonObjectConverter.h" 6 | #include "UObject/UnrealType.h" 7 | #include "InstancedStruct.h" 8 | 9 | #define LOCTEXT_NAMESPACE "FJsonSerializationModule" 10 | 11 | void FJsonSerializationModule::StartupModule() 12 | { 13 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 14 | } 15 | 16 | void FJsonSerializationModule::ShutdownModule() 17 | { 18 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 19 | // we call this function before unloading the module. 20 | } 21 | 22 | static void SerializePropertyAsJsonObjectField(const void* Data, TSharedPtr OuterObject, FProperty* Property, TSet& TraversedObjects); 23 | 24 | static void SerializeStructPropertyAsJsonObjectField(const void* InnerPropData, FStructProperty* StructProperty, TSharedPtr StructObject, TSet& TraversedObjects) 25 | { 26 | const uint8* StructPropData = StructProperty->ContainerPtrToValuePtr(InnerPropData); 27 | if (StructProperty->Struct == TBaseStructure::Get()) 28 | { 29 | const FInstancedStruct& InstancedStruct = *(FInstancedStruct*)StructPropData; 30 | for (TFieldIterator PropertyItr(InstancedStruct.GetScriptStruct()); PropertyItr; ++PropertyItr) 31 | { 32 | SerializePropertyAsJsonObjectField(InstancedStruct.GetMemory(), StructObject, *PropertyItr, TraversedObjects); 33 | } 34 | } 35 | else 36 | { 37 | for (TFieldIterator PropertyItr(StructProperty->Struct); PropertyItr; ++PropertyItr) 38 | { 39 | SerializePropertyAsJsonObjectField((void*)StructPropData, StructObject, *PropertyItr, TraversedObjects); 40 | } 41 | } 42 | } 43 | 44 | static TArray> SerializePropertyAsJsonArray(const void* Data, FArrayProperty* Property, TSet& TraversedObjects) 45 | { 46 | const uint8* PropData = Property->ContainerPtrToValuePtr(Data); 47 | FScriptArrayHelper Helper(Property, PropData); 48 | TArray> ValueArray; 49 | 50 | for (int32 i = 0, n = Helper.Num(); i < n; ++i) 51 | { 52 | const uint8* InnerPropData = Helper.GetRawPtr(i); 53 | if (FArrayProperty* ArrayProperty = CastField(Property->Inner)) // Array 54 | { 55 | TArray> InnerArray = SerializePropertyAsJsonArray(InnerPropData, ArrayProperty, TraversedObjects); 56 | ValueArray.Emplace(new FJsonValueArray(InnerArray)); 57 | } 58 | else if (FStructProperty* StructProperty = CastField(Property->Inner)) // Struct 59 | { 60 | TSharedPtr StructObject = MakeShareable(new FJsonObject); 61 | SerializeStructPropertyAsJsonObjectField(InnerPropData, StructProperty, StructObject, TraversedObjects); 62 | ValueArray.Emplace(new FJsonValueObject(StructObject)); 63 | } 64 | else if (FObjectProperty* ObjectProperty = CastField(Property->Inner)) // Object 65 | { 66 | const UObject* SubObject = ObjectProperty->GetObjectPropertyValue_InContainer(InnerPropData); 67 | if (SubObject->IsValidLowLevel() && !TraversedObjects.Contains(SubObject)) 68 | { 69 | TraversedObjects.Add(SubObject); 70 | TSharedPtr JsonSubObject = MakeShared(); 71 | for (TFieldIterator PropertyItr(SubObject->GetClass()); PropertyItr; ++PropertyItr) 72 | { 73 | SerializePropertyAsJsonObjectField(SubObject, JsonSubObject, *PropertyItr, TraversedObjects); 74 | } 75 | ValueArray.Emplace(new FJsonValueObject(JsonSubObject)); 76 | } 77 | } 78 | else 79 | { 80 | TSharedPtr JsonValue; 81 | const uint8* InnerInnerPropData = Property->Inner->ContainerPtrToValuePtr(InnerPropData); 82 | ValueArray.Emplace(FJsonObjectConverter::UPropertyToJsonValue(Property->Inner, InnerInnerPropData)); 83 | } 84 | } 85 | return ValueArray; 86 | } 87 | 88 | static void SerializePropertyAsJsonObjectField(const void* Data, TSharedPtr OuterObject, FProperty* Property, TSet& TraversedObjects) 89 | { 90 | if (Property->GetName() == "UberGraphFrame" 91 | || Property->HasAnyPropertyFlags(CPF_Transient)) 92 | { 93 | // Don't include "UberGraphFrame" or any transient properties 94 | return; 95 | } 96 | 97 | if (FArrayProperty* ArrayProperty = CastField(Property)) // Array 98 | { 99 | TArray> Values = SerializePropertyAsJsonArray(Data, ArrayProperty, TraversedObjects); 100 | OuterObject->SetArrayField(Property->GetAuthoredName(), Values); 101 | } 102 | else if (FStructProperty* StructProperty = CastField(Property)) // Struct 103 | { 104 | TSharedPtr StructObject = MakeShareable(new FJsonObject); 105 | SerializeStructPropertyAsJsonObjectField(Data, StructProperty, StructObject, TraversedObjects); 106 | OuterObject->SetObjectField(Property->GetAuthoredName(), StructObject); 107 | } 108 | else if (FObjectProperty* ObjectProperty = CastField(Property)) // Object 109 | { 110 | const UObject* SubObject = ObjectProperty->GetObjectPropertyValue_InContainer(Data); 111 | if (SubObject->IsValidLowLevel() && !TraversedObjects.Contains(SubObject)) 112 | { 113 | TraversedObjects.Add(SubObject); 114 | TSharedPtr JsonSubObject = MakeShared(); 115 | for (TFieldIterator PropertyItr(SubObject->GetClass()); PropertyItr; ++PropertyItr) 116 | { 117 | SerializePropertyAsJsonObjectField(SubObject, JsonSubObject, *PropertyItr, TraversedObjects); 118 | } 119 | OuterObject->SetObjectField(Property->GetAuthoredName(), JsonSubObject); 120 | } 121 | } 122 | else 123 | { 124 | TSharedPtr JsonValue; 125 | const uint8* PropData = Property->ContainerPtrToValuePtr(Data); 126 | OuterObject->SetField(Property->GetAuthoredName(), FJsonObjectConverter::UPropertyToJsonValue(Property, PropData)); 127 | } 128 | } 129 | 130 | TSharedPtr FJsonSerializationModule::SerializeUObjectToJson(const UObject* Object) 131 | { 132 | TSet TraversedObjects; 133 | TraversedObjects.Add(Object); 134 | 135 | TSharedPtr JsonObject = MakeShared(); 136 | 137 | for (TFieldIterator PropertyItr(Object->GetClass()); PropertyItr; ++PropertyItr) 138 | { 139 | SerializePropertyAsJsonObjectField(Object, JsonObject, *PropertyItr, TraversedObjects); 140 | } 141 | 142 | return JsonObject; 143 | } 144 | 145 | #undef LOCTEXT_NAMESPACE 146 | 147 | IMPLEMENT_MODULE(FJsonSerializationModule, JsonSerialization) -------------------------------------------------------------------------------- /Source/JsonSerialization/Private/JsonSerializationBlueprintFunctionLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "JsonSerializationBlueprintFunctionLibrary.h" 5 | 6 | #include "JsonSerialization.h" 7 | #include "JsonObjectWrapper.h" 8 | 9 | FJsonObjectWrapper UJsonSerializationBlueprintFunctionLibrary::ObjectToJson(const UObject* Object) 10 | { 11 | FJsonObjectWrapper JsonWrapper; 12 | JsonWrapper.JsonObject = FJsonSerializationModule::SerializeUObjectToJson(Object); 13 | return JsonWrapper; 14 | } 15 | -------------------------------------------------------------------------------- /Source/JsonSerialization/Public/JsonSerialization.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Dom/JsonObject.h" 7 | #include "Modules/ModuleManager.h" 8 | 9 | class JSONSERIALIZATION_API FJsonSerializationModule : public IModuleInterface 10 | { 11 | public: 12 | 13 | /** IModuleInterface implementation */ 14 | virtual void StartupModule() override; 15 | virtual void ShutdownModule() override; 16 | 17 | static TSharedPtr SerializeUObjectToJson(const UObject* Object); 18 | }; 19 | -------------------------------------------------------------------------------- /Source/JsonSerialization/Public/JsonSerializationBlueprintFunctionLibrary.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Kismet/BlueprintFunctionLibrary.h" 7 | #include "JsonObjectWrapper.h" 8 | #include "JsonSerializationBlueprintFunctionLibrary.generated.h" 9 | 10 | /** 11 | * 12 | */ 13 | UCLASS() 14 | class JSONSERIALIZATION_API UJsonSerializationBlueprintFunctionLibrary : public UBlueprintFunctionLibrary 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | UFUNCTION(BlueprintPure, Category = "Json Serialization") 20 | static FJsonObjectWrapper ObjectToJson(const UObject* Object); 21 | }; 22 | --------------------------------------------------------------------------------