├── JSONQuery.uplugin ├── Resources └── Icon128.png └── Source └── JSONQuery ├── Classes └── JsonFieldData.h ├── JSONQuery.Build.cs └── Private ├── JSONQueryModule.cpp ├── JSONQueryPrivatePCH.h └── JsonFieldData.cpp /JSONQuery.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion" : 3, 3 | 4 | "FriendlyName" : "JSON Query", 5 | "Version" : 5, 6 | "VersionName" : "0.91", 7 | "CreatedBy" : "Stefander", 8 | "CreatedByURL" : "http://stefander.nl/", 9 | "EngineVersion" : "4.8.0", 10 | "Description" : "Exposes nodes to Blueprint that allow you to easily post and request pages on webservers through the JSON protocol.", 11 | "Category" : "Web", 12 | "EnabledByDefault" : true, 13 | 14 | "Modules" : 15 | [ 16 | { 17 | "Name" : "JSONQuery", 18 | "Type" : "Runtime" 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marynate/JSONQuery_UE4/719c8cf8e0206b65c58d98db44d7554d6138b966/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/JSONQuery/Classes/JsonFieldData.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. 2 | #pragma once 3 | #include "JsonFieldData.generated.h" 4 | 5 | // Generate a delegate for the OnGetResult event 6 | DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGetResult); 7 | 8 | // Generate a delegate for the OnFailed event 9 | DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnFailed); 10 | 11 | UCLASS(BlueprintType, Blueprintable) 12 | class UJsonFieldData : public UObject 13 | { 14 | GENERATED_UCLASS_BODY() 15 | 16 | private: 17 | /* Internal bind method for the IHTTPRequest::OnProcessRequestCompleted() event */ 18 | void OnReady(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful); 19 | 20 | /* Resets the current post data */ 21 | void Reset(); 22 | 23 | /* Prefixes the input URL with http:// if needed */ 24 | static FString CreateURL(FString inputURL); 25 | 26 | void WriteObject(TSharedRef> writer, FString key, FJsonValue* value); 27 | public: 28 | UObject* contextObject; 29 | 30 | /* The actual field data */ 31 | TSharedPtr Data; 32 | 33 | /* Contains the actual page content, as a string */ 34 | UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "JSON") 35 | FString Content; 36 | 37 | /* Event which triggers when the content has been retrieved */ 38 | UPROPERTY(BlueprintAssignable, Category = "JSON") 39 | FOnGetResult OnGetResult; 40 | 41 | /* Event which triggers when the request failed */ 42 | UPROPERTY(BlueprintAssignable, Category = "JSON") 43 | FOnFailed OnFailed; 44 | 45 | /* Creates a new post data object */ 46 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Create JSON Data", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "JSON") 47 | static UJsonFieldData* Create(UObject* WorldContextObject); 48 | 49 | /* Adds string data to the post data */ 50 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Add String Field"), Category = "JSON") 51 | UJsonFieldData* SetString(const FString& key, const FString& value); 52 | 53 | /* Adds a string array to the post data */ 54 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Add String Array Field"), Category = "JSON") 55 | UJsonFieldData* SetStringArray(const FString& key, const TArray arrayData); 56 | 57 | /* Sets nested object data to the post array */ 58 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Add Data Field"), Category = "JSON") 59 | UJsonFieldData* SetObject(const FString& key, const UJsonFieldData* objectData); 60 | 61 | /* Adds a new post data field to the specified data */ 62 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Add Object Array Field"), Category = "JSON") 63 | UJsonFieldData* SetObjectArray(const FString& key, const TArray arrayData); 64 | 65 | /* Gets string data from the post data */ 66 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Get String Field"), Category = "JSON") 67 | FString GetString(const FString& key) const; 68 | 69 | /* Gets a string array with the specified key */ 70 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Get String Array Field", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "JSON") 71 | TArray GetStringArray(const FString& key); 72 | 73 | /* Fetches nested post data from the post data */ 74 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Data Field", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "JSON") 75 | UJsonFieldData* GetObject(const FString& key); 76 | 77 | /* Gets an array with post data with the specified key */ 78 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Object Array Field", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "JSON") 79 | TArray GetObjectArray(UObject* WorldContextObject, const FString& key); 80 | 81 | /* Get all keys from the object */ 82 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Object Keys", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "JSON") 83 | TArray GetObjectKeys(UObject* WorldContextObject); 84 | 85 | /* Creates new data from the input string */ 86 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "From String"), Category = "JSON") 87 | void FromString(const FString& dataString); 88 | 89 | /* Creates new data from text json file */ 90 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "From File"), Category = "JSON") 91 | void FromFile(const FString& FilePath); 92 | 93 | /* Posts a request with the supplied post data to the specified page */ 94 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Post JSON Request", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "JSON") 95 | void PostRequest(UObject* WorldContextObject, const FString& url); 96 | 97 | /* Requests a page from the internet with a JSON response */ 98 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get JSON Request", HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"), Category = "JSON") 99 | static UJsonFieldData* GetRequest(UObject* WorldContextObject, const FString& url); 100 | }; -------------------------------------------------------------------------------- /Source/JSONQuery/JSONQuery.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. 2 | using UnrealBuildTool; 3 | 4 | public class JSONQuery : ModuleRules 5 | { 6 | public JSONQuery(TargetInfo Target) 7 | { 8 | PublicDependencyModuleNames.AddRange( 9 | new string[] { 10 | "Core", 11 | "CoreUObject", 12 | "Engine", 13 | "HTTP", 14 | "Json" 15 | } 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/JSONQuery/Private/JSONQueryModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. 2 | #include "JSONQueryPrivatePCH.h" 3 | 4 | IMPLEMENT_MODULE(FDefaultGameModuleImpl, JSONQuery); -------------------------------------------------------------------------------- /Source/JSONQuery/Private/JSONQueryPrivatePCH.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Engine.h" 6 | #include "Http.h" 7 | #include "Delegate.h" 8 | #include "Map.h" 9 | #include "Json.h" 10 | #include "JSONQueryClasses.h" -------------------------------------------------------------------------------- /Source/JSONQuery/Private/JsonFieldData.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. 2 | #include "JSONQueryPrivatePCH.h" 3 | 4 | ////////////////////////////////////////////////////////////////////////// 5 | // UJsonFieldData 6 | 7 | /** 8 | * Constructor 9 | */ 10 | UJsonFieldData::UJsonFieldData(const class FObjectInitializer& PCIP) 11 | : Super(PCIP) { 12 | Reset(); 13 | } 14 | 15 | /** 16 | * Grabs a page from the internet 17 | * 18 | * @param WorldContextObject The current context 19 | * @param url The URL to request 20 | * 21 | * @return A pointer to the newly created post data 22 | */ 23 | UJsonFieldData* UJsonFieldData::GetRequest(UObject* WorldContextObject, const FString &url) { 24 | // Create new page data for the response 25 | UJsonFieldData* dataObj = Create(WorldContextObject); 26 | 27 | // Create the HTTP request 28 | TSharedRef< IHttpRequest > HttpRequest = FHttpModule::Get().CreateRequest(); 29 | HttpRequest->SetVerb("GET"); 30 | HttpRequest->SetURL(CreateURL(url)); 31 | HttpRequest->OnProcessRequestComplete().BindUObject(dataObj, &UJsonFieldData::OnReady); 32 | 33 | // Execute the request 34 | HttpRequest->ProcessRequest(); 35 | 36 | // Return the page data 37 | return dataObj; 38 | } 39 | 40 | /** 41 | * Create a new instance of the UJsonFieldData class, for use in Blueprint graphs. 42 | * 43 | * @param WorldContextObject The current context 44 | * 45 | * @return A pointer to the newly created post data 46 | */ 47 | UJsonFieldData* UJsonFieldData::Create(UObject* WorldContextObject) { 48 | // Get the world object from the context 49 | UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject); 50 | 51 | // Construct the object and return it 52 | UJsonFieldData* fieldData = (UJsonFieldData*)StaticConstructObject(UJsonFieldData::StaticClass()); 53 | fieldData->contextObject = WorldContextObject; 54 | return fieldData; 55 | } 56 | 57 | /** 58 | * Prefixes the input URL with http:// if necessary 59 | * 60 | * @param inputURL The input URL 61 | * 62 | * @return The output URL 63 | */ 64 | FString UJsonFieldData::CreateURL(FString inputURL) { 65 | if (!inputURL.StartsWith("http")) { 66 | return "http://" + inputURL; 67 | } 68 | 69 | return inputURL; 70 | } 71 | 72 | /** 73 | * This function will write the supplied key and value to the JsonWriter 74 | * 75 | * @param writer The JsonWriter to use 76 | * @param key Object key 77 | * @param value Object value 78 | * 79 | */ 80 | void UJsonFieldData::WriteObject(TSharedRef> writer, FString key, FJsonValue* value) { 81 | if (value->Type == EJson::String) { 82 | // Write simple string entry, don't a key when it isn't set 83 | if (key.Len() > 0) { 84 | writer->WriteValue(key, value->AsString()); 85 | } else { 86 | writer->WriteValue(value->AsString()); 87 | } 88 | } else if (value->Type == EJson::Object) { 89 | // Write object entry 90 | if (key.Len() > 0) { 91 | writer->WriteObjectStart(key); } 92 | else { 93 | writer->WriteObjectStart(); } 94 | 95 | // Loop through all the values in the object data 96 | TSharedPtr objectData = value->AsObject(); 97 | for (auto objectValue = objectData->Values.CreateIterator(); objectValue; ++objectValue) { 98 | // Using recursion to write the key and value to the writer 99 | WriteObject(writer, objectValue.Key(), objectValue.Value().Get()); 100 | } 101 | 102 | writer->WriteObjectEnd(); 103 | } else if (value->Type == EJson::Array) { 104 | // Process array entry 105 | writer->WriteArrayStart(key); 106 | 107 | TArray> objectArray = value->AsArray(); 108 | for (int32 i = 0; i < objectArray.Num(); i++) { 109 | // Use recursion with an empty key to process all the values in the array 110 | WriteObject(writer, "", objectArray[i].Get()); 111 | } 112 | 113 | writer->WriteArrayEnd(); 114 | } 115 | } 116 | 117 | /** 118 | * Posts the current request data to the internet 119 | * 120 | * @param WorldContextObject The current context 121 | * @param url The URL to post to 122 | * 123 | */ 124 | void UJsonFieldData::PostRequest(UObject* WorldContextObject, const FString &url) { 125 | FString outStr; 126 | TSharedRef> JsonWriter = TJsonWriterFactory::Create(&outStr); 127 | 128 | // Start writing the response 129 | WriteObject(JsonWriter, "", new FJsonValueObject(Data)); 130 | JsonWriter->Close(); 131 | 132 | // Log the post data for the user (OPTIONAL) 133 | UE_LOG(LogTemp, Warning, TEXT("Post data: %s"), *outStr); 134 | 135 | // Create the post request with the generated data 136 | TSharedRef< IHttpRequest > HttpRequest = FHttpModule::Get().CreateRequest(); 137 | HttpRequest->SetVerb("POST"); 138 | HttpRequest->SetURL(CreateURL(url)); 139 | HttpRequest->SetHeader("Content-Type", "application/json"); 140 | HttpRequest->SetContentAsString(outStr); 141 | HttpRequest->OnProcessRequestComplete().BindUObject(this, &UJsonFieldData::OnReady); 142 | 143 | // Execute the request 144 | HttpRequest->ProcessRequest(); 145 | } 146 | 147 | /** 148 | * Adds the supplied string to the post data, under the given key 149 | * 150 | * @param key Key 151 | * @param value Object value 152 | * 153 | * @return The object itself 154 | */ 155 | UJsonFieldData* UJsonFieldData::SetString(const FString& key, const FString& value) { 156 | Data->SetStringField(*key,*value); 157 | return this; 158 | } 159 | 160 | /** 161 | * Adds the supplied object to the post data, under the given key 162 | * 163 | * @param key Key 164 | * @param objectData Object data 165 | * 166 | * @return The object itself 167 | */ 168 | UJsonFieldData* UJsonFieldData::SetObject(const FString& key, const UJsonFieldData* objectData) { 169 | Data->SetObjectField(*key, objectData->Data); 170 | return this; 171 | } 172 | 173 | /** 174 | * Adds the supplied object array to the post data, under the given key 175 | * 176 | * @param key Key 177 | * @param objectData Array of object data 178 | * 179 | * @return The object itself 180 | */ 181 | UJsonFieldData* UJsonFieldData::SetObjectArray(const FString& key, const TArray objectData) { 182 | TArray> *dataArray = new TArray>(); 183 | 184 | // Loop through the array and create new shared FJsonValueObject instances for every FJsonObject 185 | for (int32 i = 0; i < objectData.Num(); i++) { 186 | dataArray->Add(MakeShareable(new FJsonValueObject(objectData[i]->Data))); 187 | } 188 | 189 | Data->SetArrayField(*key, *dataArray); 190 | return this; 191 | } 192 | 193 | /** 194 | * Adds the supplied string array to the post data, under the given key 195 | * 196 | * @param key Key 197 | * @param objectData Array of strings 198 | * 199 | * @return The object itself 200 | */ 201 | UJsonFieldData* UJsonFieldData::SetStringArray(const FString& key, const TArray stringData) { 202 | TArray> *dataArray = new TArray>(); 203 | 204 | // Loop through the input array and add new shareable FJsonValueString instances to the data array 205 | for (int32 i=0; i < stringData.Num(); i++) { 206 | dataArray->Add(MakeShareable(new FJsonValueString(stringData[i]))); 207 | } 208 | 209 | Data->SetArrayField(*key, *dataArray); 210 | return this; 211 | } 212 | 213 | /** 214 | * Gets the post data object from the post data with the given key 215 | * 216 | * @param WorldContextObject Array of strings 217 | * @param key Key 218 | * 219 | * @return The object itself 220 | */ 221 | UJsonFieldData* UJsonFieldData::GetObject(const FString& key) { 222 | UJsonFieldData* fieldObj = NULL; 223 | 224 | // Try to get the object field from the data 225 | const TSharedPtr *outPtr; 226 | if (!Data->TryGetObjectField(*key, outPtr)) { 227 | // Throw an error and return NULL when the key could not be found 228 | UE_LOG(LogJson, Error, TEXT("Entry '%s' not found in the field data!"), *key); 229 | return NULL; 230 | } 231 | 232 | // Create a new field data object and assign the data 233 | fieldObj = UJsonFieldData::Create(contextObject); 234 | fieldObj->Data = *outPtr; 235 | 236 | // Return the newly created object 237 | return fieldObj; 238 | } 239 | 240 | /** 241 | * Gets a string array from the post data with the given key 242 | * 243 | * @param key Key 244 | * 245 | * @return The requested array of strings 246 | */ 247 | TArray UJsonFieldData::GetStringArray(const FString& key) { 248 | TArray stringArray; 249 | 250 | // Try to get the array field from the post data 251 | const TArray> *arrayPtr; 252 | if (Data->TryGetArrayField(*key, arrayPtr)) { 253 | // Iterate through the array and use the string value from all the entries 254 | for (int32 i=0; i < arrayPtr->Num(); i++) { 255 | stringArray.Add((*arrayPtr)[i]->AsString()); 256 | } 257 | } else { 258 | // Throw an error when the entry could not be found in the field data 259 | UE_LOG(LogJson, Error, TEXT("Array entry '%s' not found in the field data!"), *key); 260 | } 261 | 262 | // Return the array, if unsuccessful the array will be empty 263 | return stringArray; 264 | } 265 | 266 | /** 267 | * Gets an object array from the post data with the given key 268 | * 269 | * @param key Key 270 | * 271 | * @return The requested post data objects 272 | */ 273 | TArray UJsonFieldData::GetObjectArray(UObject* WorldContextObject, const FString& key) { 274 | TArray objectArray; 275 | 276 | // Try to fetch and assign the array to the array pointer 277 | const TArray> *arrayPtr; 278 | if (Data->TryGetArrayField(*key, arrayPtr)) { 279 | // Iterate through the input array and create new post data objects for every entry and add them to the objectArray 280 | for (int32 i = 0; i < arrayPtr->Num(); i++) { 281 | UJsonFieldData* pageData = Create(WorldContextObject); 282 | pageData->Data = (*arrayPtr)[i]->AsObject(); 283 | objectArray.Add(pageData); 284 | } 285 | } 286 | else { 287 | // Throw an error, since the value with the supplied key could not be found 288 | UE_LOG(LogJson, Error, TEXT("Array entry '%s' not found in the field data!"), *key); 289 | } 290 | 291 | // Return the array, will be empty if unsuccessful 292 | return objectArray; 293 | } 294 | 295 | /** 296 | * Gets the keys from the supplied object 297 | * 298 | * @param key Key 299 | * 300 | * @return Array of keys 301 | */ 302 | TArray UJsonFieldData::GetObjectKeys(UObject* WorldContextObject) { 303 | TArray stringArray; 304 | 305 | for (auto currJsonValue = Data->Values.CreateConstIterator(); currJsonValue; ++currJsonValue) { 306 | stringArray.Add((*currJsonValue).Key); 307 | } 308 | 309 | // Return the array, will be empty if unsuccessful 310 | return stringArray; 311 | } 312 | 313 | /** 314 | * Tries to get a string from the field data by key, returns the string when successful 315 | * 316 | * @param key Key 317 | * 318 | * @return The requested string, empty if failed 319 | */ 320 | FString UJsonFieldData::GetString(const FString& key) const { 321 | FString outString; 322 | 323 | // If the current post data isn't valid, return an empty string 324 | if (!Data->TryGetStringField(*key, outString)) { 325 | UE_LOG(LogJson, Error, TEXT("Entry '%s' not found in the field data!"), *key); 326 | return ""; 327 | } 328 | 329 | return outString; 330 | } 331 | 332 | /** 333 | * Resets the current page data 334 | * 335 | */ 336 | void UJsonFieldData::Reset() { 337 | // If the post data is valid 338 | if (Data.IsValid()) { 339 | // Clear the current post data 340 | Data.Reset(); 341 | } 342 | 343 | // Create a new JSON object 344 | Data = MakeShareable(new FJsonObject()); 345 | } 346 | 347 | /** 348 | * Creates new data from the 349 | * 350 | * @param key Key 351 | * 352 | * @return The requested string, empty if failed 353 | */ 354 | void UJsonFieldData::FromString(const FString& dataString) { 355 | TSharedRef> JsonReader = TJsonReaderFactory::Create(dataString); 356 | 357 | // Deserialize the JSON data 358 | bool isDeserialized = FJsonSerializer::Deserialize(JsonReader, Data); 359 | 360 | if (!isDeserialized || !Data.IsValid()) { 361 | UE_LOG(LogJson, Error, TEXT("JSON data is invalid! Input:\n'%s'"), *dataString); 362 | } 363 | 364 | // Assign the request content 365 | Content = dataString; 366 | } 367 | 368 | /** 369 | * Creates new data from the 370 | * 371 | * @param FilePath Text Json File in game content folder 372 | * 373 | * @return JsonFieldData Object 374 | */ 375 | void UJsonFieldData::FromFile(const FString& FilePath) { 376 | 377 | FString Result; 378 | FString FullJsonPath = FPaths::ConvertRelativePathToFull(FPaths::GameContentDir() / FilePath); 379 | if (!FFileHelper::LoadFileToString(Result, *FullJsonPath)) { 380 | UE_LOG(LogJson, Error, TEXT("Can't load json data from %s"), *FilePath); 381 | } 382 | FromString(Result); 383 | } 384 | 385 | /** 386 | * Callback for IHttpRequest::OnProcessRequestComplete() 387 | * 388 | * @param Request HTTP request pointer 389 | * @param Response Response pointer 390 | * @param bWasSuccessful Whether the request was successful or not 391 | * 392 | */ 393 | void UJsonFieldData::OnReady(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { 394 | if (!bWasSuccessful) { 395 | UE_LOG(LogJson, Error, TEXT("Response was invalid! Please check the URL.")); 396 | 397 | // Broadcast the failed event 398 | OnFailed.Broadcast(); 399 | return; 400 | } 401 | 402 | // Process the string 403 | FromString(Response->GetContentAsString()); 404 | 405 | // Broadcast the result event 406 | OnGetResult.Broadcast(); 407 | } --------------------------------------------------------------------------------