{"id":86236,"date":"2020-02-03T11:27:52","date_gmt":"2020-02-03T11:27:52","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=86236"},"modified":"2021-07-29T19:44:05","modified_gmt":"2021-07-29T19:44:05","slug":"saving-game-data-with-unity","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/saving-game-data-with-unity\/","title":{"rendered":"Saving Game Data in Unity: PlayerPrefs for Simple Values and C# Serialization for Complex Objects"},"content":{"rendered":"<p><b>Unity games typically need to persist data between sessions &#8211; player progress, settings, high scores, inventory, unlocked content. Unity provides two main built-in approaches: (1) PlayerPrefs &#8211; a simple key-value store accessible via PlayerPrefs.SetInt\/GetInt, SetFloat\/GetFloat, SetString\/GetString, persisted automatically to platform-appropriate storage (Registry on Windows, NSUserDefaults on iOS\/macOS, SharedPreferences on Android). Easy to use but limited to primitive types &#8211; you can&#8217;t store arrays, complex objects, or custom classes directly; (2) C# serialization &#8211; writing complex objects to a file as binary or text. The article covers BinaryFormatter-based serialization for full object graphs. MODERN CONTEXT NOTE: since the article was written, BinaryFormatter has been deprecated by Microsoft due to security concerns (arbitrary code execution via deserialisation of untrusted input). For new Unity projects, use JsonUtility (Unity&#8217;s built-in, lightweight), System.Text.Json (for more advanced JSON scenarios), or a dedicated serialisation library (Newtonsoft.Json, MessagePack, Protocol Buffers) instead. The techniques shown here remain conceptually valid &#8211; save to file, read from file, handle errors &#8211; just substitute the serialisation mechanism. This article walks through both approaches with complete scripts demonstrating save and load operations.<\/b><\/p>\n<p>Back in the day, many games had little need for saving data since you could finish a game in about an hour. At best, a game would save a high score and leave it at that. But it didn&#8217;t take long for technology to improve, and thus games got longer and more complex. The need for saving all kinds of data, ranging from how much progress the player made in-game or their character&#8217;s statistics, became universal whether the game be a simple, linear adventure from beginning to end or it consists of a huge open world. Many games require multiple play sessions to see to the end, and it&#8217;s now expected that developers include ways to save the game and come back later. Only when a game is short enough that it can be completed in a single session does the need for saving data diminish, but even in those scenarios, the feature can be very handy.<\/p>\n<p>Unity provides two ways to save a game&#8217;s data. They can be quickly described as \u201cthe easy way\u201d and \u201cthe not so easy way.\u201d The easy way involves Unity&#8217;s built-in <em>PlayerPrefs <\/em>system. Give a value to a key, call <em>Save<\/em>, and you&#8217;re done. On the other hand, the not so easy way involves serializing data and writing to a file for later use. Both methods have their pros and cons, which will be covered as the practices are explored.<\/p>\n<p>There&#8217;s minimal setup involved in testing these out. You just need a project and a couple of scripts. For this project, you will want to use the 2D template for the easiest setup as shown in Figure 1.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"846\" height=\"484\" class=\"wp-image-86237\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/02\/word-image.jpeg\" \/> Figure 1: Project creation<\/p>\n<p>Once you create the project, you&#8217;ll need two scripts. You create scripts by right-clicking in the <em>Assets <\/em>window and selecting <em>Create-&gt;C# Script<\/em> as shown in Figure 2. Call these scripts <em>SavePrefs <\/em>and <em>SaveSerial<\/em>.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"626\" height=\"757\" class=\"wp-image-86238\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/02\/word-image-1.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 2: Script creation<\/p>\n<p>To see what saving a game entails, start with the <em>SavePrefs <\/em>script<em>. <\/em>Double-click the script to open Visual Studio.<\/p>\n<h2>PlayerPrefs \u2013 The Easy Way<\/h2>\n<p>To begin, the <code>Start<\/code> and <code>Update<\/code> methods can be commented out or deleted as they will not be used to demonstrate the save functionality. Next, some variables to hold the values to save will be needed.<\/p>\n<pre class=\"lang:c# theme:vs2012\">int intToSave;\r\nfloat floatToSave;\r\nstring stringToSave = \"\";<\/pre>\n<p>Next, the <code>OnGui<\/code> method will create a Graphical User Interface (GUI) from code to manipulate these values. Two buttons created in the method will increase <code>intToSave<\/code> and <code>floatToSave<\/code>, and a text field will be made for <code>stringToSave<\/code>. The code will also create a few labels made to show the current values of these variables. Finally, three more buttons will be made that save, load, and reset data.<\/p>\n<pre class=\"lang:c# theme:vs2012 \">void OnGUI()\r\n{\r\n\tif (GUI.Button(new Rect(0, 0, 125, 50), \"Raise Integer\"))\r\n\t\tintToSave++;\r\n\tif (GUI.Button(new Rect(0, 100, 125, 50), \"Raise Float\"))\r\n\t\tfloatToSave += 0.1f;\r\n\tstringToSave = GUI.TextField(new Rect(0, 200, 125, 25), \r\n               stringToSave, 15);\r\n\tGUI.Label(new Rect(375, 0, 125, 50), \"Integer value is \" \r\n               + intToSave);\r\n\tGUI.Label(new Rect(375, 100, 125, 50), \"Float value is \" \r\n               + floatToSave.ToString(\"F1\"));\r\n\tGUI.Label(new Rect(375, 200, 125, 50), \"String value is \" \r\n              + stringToSave);\r\n\tif (GUI.Button(new Rect(750, 0, 125, 50), \"Save Your Game\"))\r\n\t\tSaveGame();\r\n\tif (GUI.Button(new Rect(750, 100, 125, 50), \r\n                \"Load Your Game\"))\r\n\t\tLoadGame();\r\n\tif (GUI.Button(new Rect(750, 200, 125, 50), \r\n                \"Reset Save Data\"))\r\n\t\tResetData();\r\n}<\/pre>\n<p>The last three buttons all call methods whenever they are clicked, but those methods have not been defined yet. This will be fixed now, starting with the <code>SaveGame<\/code> method.<\/p>\n<pre class=\"lang:c# theme:vs2012\">void SaveGame()\r\n{\r\n\tPlayerPrefs.SetInt(\"SavedInteger\", intToSave);\r\n\tPlayerPrefs.SetFloat(\"SavedFloat\", floatToSave);\r\n\tPlayerPrefs.SetString(\"SavedString\", stringToSave);\r\n\tPlayerPrefs.Save();\r\n\tDebug.Log(\"Game data saved!\");\r\n}<\/pre>\n<p>No, your eyes do not deceive you. The actual act of saving your game takes only a few lines of code. So, what&#8217;s happening? Well, as promised, <code>PlayerPrefs<\/code> saves the player&#8217;s game. First, you must set some variables for <code>PlayerPrefs<\/code> to save. As seen above, three variables were set, all of them given a name, or key, followed by the variable to save. Once <code>PlayerPrefs<\/code> is given all its information, <em>Save <\/em>is called and, as you may have guessed, saves the data. A message is also printed to Unity&#8217;s debug console as a little note to the developer saying the save was successful.<\/p>\n<p>You may be wondering where this save data is on the computer. On Windows, <code>PlayerPrefs<\/code> can be found in the Registry under <em>HKEY_CURRENT_USER\\Software\\Unity\\UnityEditor\\[company name]\\[project name]<\/em> (Figure 3), where <em>company name <\/em>and <em>project name <\/em>are names set up in the project settings. Bear in mind this is the location for when the game was run from the editor. In an exe they can be found at <em>HKEY_CURRENT_USER\\Software\\[company name]\\[project name]<\/em>. On Mac OS, according to the Unity documentation, the <em>PlayerPrefs <\/em>is found at <em>~\/Library\/Preferences folder, <\/em>in a file named<em> unity.[company name].[product name].plist<\/em>.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"904\" height=\"283\" class=\"wp-image-86239\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/02\/word-image-2.jpeg\" \/> Figure 3: <em>PlayerPrefs <\/em>variables in the Windows Registry<\/p>\n<p>Loading data is essentially saving data done in reverse. You set your variables of choice to whatever is in <code>PlayerPrefs<\/code><em>, <\/em>and you&#8217;re good to go. A good practice is to make sure that the <code>PlayerPrefs<\/code> for your game has at least one of the keys you&#8217;re looking for. In other words, you&#8217;re checking that there&#8217;s any save data to be found. The code sample below uses <code>HasKey<\/code> to search for one of the keys declared in the <code>SaveGame<\/code> method, those keys being <code>SavedInteger<\/code><em>, <\/em><code>SavedFloat<\/code><em>, <\/em>and <code>SavedString<\/code>. Looking for just one will be sufficient. So long as <code>PlayerPrefs<\/code> has one of those, it&#8217;s safe to assume it will have the remaining data. Otherwise, it will print an error to the Unity console.<\/p>\n<pre class=\"lang:c# theme:vs2012\">void LoadGame()\r\n{\r\n\tif (PlayerPrefs.HasKey(\"SavedInteger\"))\r\n\t{\r\n\t\tintToSave = PlayerPrefs.GetInt(\"SavedInteger\");\r\n\t\tfloatToSave = PlayerPrefs.GetFloat(\"SavedFloat\");\r\n\t\tstringToSave = PlayerPrefs.GetString(\"SavedString\");\r\n\t\tDebug.Log(\"Game data loaded!\");\r\n\t}\r\n\telse\r\n\t\tDebug.LogError(\"There is no save data!\");\r\n}<\/pre>\n<p>Finally, if you wish to remove the save data stored in <code>PlayerPrefs<\/code>, all you need to call is <code>PlayerPrefs.DeleteAll<\/code> and the work is complete. In the following method, <code>DeleteAll<\/code> is put to use along with resetting the variables and ending by printing a message to the debug console.<\/p>\n<pre class=\"lang:c# theme:vs2012\">void ResetData()\r\n{\r\n\tPlayerPrefs.DeleteAll();\r\n\tintToSave = 0;\r\n\tfloatToSave = 0.0f;\r\n\tstringToSave = \"\";\r\n\tDebug.Log(\"Data reset complete\");\r\n}<\/pre>\n<p>To try it out in-game, save your code and return to the Unity editor. Attach the <em>SavePrefs <\/em>script to an object, such as <em>Main Camera<\/em> as shown in Figure 4.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"741\" height=\"201\" class=\"wp-image-86240\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/02\/word-image-3.jpeg\" \/> Figure 4: Attaching the <em>SavePrefs <\/em>script.<\/p>\n<p>Begin playing the game and tinkering with the GUI on-screen, changing variables to whatever you wish. When ready, save your game. Then try stopping and replaying the game, this time clicking the <em>Load Your Game <\/em>button. Provided everything works correctly, you should see the variables immediately change to whatever was saved to <code>PlayerPrefs<\/code>. In addition, you can wipe <code>PlayerPrefs<\/code> clean by clicking the <em>Reset Save Data <\/em>button. Figure 5 shows the game in action.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"885\" height=\"410\" class=\"wp-image-86241\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/02\/word-image-4.jpeg\" \/> Figure 5: The project in action, using <em>PlayerPrefs<\/em><\/p>\n<p>This method seems simple and effective, so why wouldn&#8217;t you use <code>PlayerPrefs<\/code> all the time? Well, <code>PlayerPrefs<\/code> is one of the least secure ways to save your data, and thus you wouldn&#8217;t want to save anything in <code>PlayerPrefs<\/code> that you absolutely do not want a potential player tampering with. This could be things like how much in-game currency the player currently possesses or stats in a role-playing game. As the name implies, best practices for <code>PlayerPrefs<\/code> is typically storing a player&#8217;s preferences and other trivial data. For example, if you&#8217;re letting the user customize the look and size of the game&#8217;s UI, <code>PlayerPrefs<\/code> would be an excellent way to store those preferences.<\/p>\n<p>There&#8217;s also the issue of flexibility. The project&#8217;s <code>SaveGame<\/code> method saves an int, float, and string. These are all the data types you can save to <code>PlayerPrefs<\/code>, so if you wish to save variables of other types, you may be out of luck. Fortunately, there is the \u201cnot so easy way\u201d of doing things that allows more flexibility in what you can save, not to mention a little extra security.<\/p>\n<h2>Serialization \u2013 The Not So Easy Way<\/h2>\n<p>Open up the <em>SaveSerial <\/em>script to begin trying out the next method. Much of the code will be the same as the last script with some minor differences to prove certain points. Here&#8217;s the variables and <code>OnGUI<\/code> method that will be used. All remaining methods will be different.<\/p>\n<pre class=\"lang:c# theme:vs2012 \">int intToSave;\r\nfloat floatToSave;\r\nbool boolToSave;\r\nvoid OnGUI()\r\n{\r\n\tif (GUI.Button(new Rect(0, 0, 125, 50), \"Raise Integer\"))\r\n\t\tintToSave++;\r\n\tif (GUI.Button(new Rect(0, 100, 125, 50), \"Raise Float\"))\r\n\t\tfloatToSave += 0.1f;\r\n\tif (GUI.Button(new Rect(0, 200, 125, 50), \"Change Bool\"))\r\n\t\tboolToSave = boolToSave ? boolToSave \r\n                       = false : boolToSave = true;\r\n\tGUI.Label(new Rect(375, 0, 125, 50), \"Integer value is \" \r\n                + intToSave);\r\n\tGUI.Label(new Rect(375, 100, 125, 50), \"Float value is \" \r\n                + floatToSave.ToString(\"F1\"));\r\n\tGUI.Label(new Rect(375, 200, 125, 50), \"Bool value is \" \r\n                + boolToSave);\r\n\tif (GUI.Button(new Rect(750, 0, 125, 50), \"Save Your Game\"))\r\n\t\tSaveGame();\r\n\tif (GUI.Button(new Rect(750, 100, 125, 50), \r\n                \"Load Your Game\"))\r\n\t\tLoadGame();\r\n\tif (GUI.Button(new Rect(750, 200, 125, 50), \r\n                \"Reset Save Data\"))\r\n\t\tResetData();\r\n}<\/pre>\n<p>To start, a few using statements will be needed to save data using serialization.<\/p>\n<pre class=\"lang:c# theme:vs2012\">using System;\r\nusing System.Runtime.Serialization.Formatters.Binary;\r\nusing System.IO;<\/pre>\n<p>Next, in order to save data, a new class within this script will be created. This class will be made serializable and will consist of the data to be saved.<\/p>\n<pre class=\"lang:c# theme:vs2012\">[Serializable]\r\nclass SaveData\r\n{\r\n    public int savedInt;\r\n    public float savedFloat;\r\n    public bool savedBool;\r\n}<\/pre>\n<p>I&#8217;ve included a screenshot (Figure 6) here to make it easier to understand where this code will go:<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"464\" height=\"426\" class=\"wp-image-86242\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/02\/word-image-5.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 6: <em>SaveSerial <\/em>script<\/p>\n<p>The goal now is to create the remaining methods that will allow you to save, load, and reset save data. You might notice that the three variables in the <code>SaveData<\/code> class correspond with the three variables in <code>SaveSerial<\/code>. Saving data will work by passing <code>SaveSerial's<\/code> variable values into <code>SaveData<\/code> and then serializing the <code>SaveData<\/code> class itself. Returning to the <code>SaveSerial<\/code> class, create the following method under the <code>OnGUI<\/code> method.<\/p>\n<pre class=\"lang:c# theme:vs2012\">void SaveGame()\r\n{\r\n\tBinaryFormatter bf = new BinaryFormatter(); \r\n\tFileStream file = File.Create(Application.persistentDataPath \r\n                 + \"\/MySaveData.dat\"); \r\n\tSaveData data = new SaveData();\r\n\tdata.savedInt = intToSave;\r\n\tdata.savedFloat = floatToSave;\r\n\tdata.savedBool = boolToSave;\r\n\tbf.Serialize(file, data);\r\n\tfile.Close();\r\n\tDebug.Log(\"Game data saved!\");\r\n}<\/pre>\n<p>The <code>BinaryFormatter<\/code> is used to perform the act of serialization and deserialization. When serializing the data, <code>BinaryFormatter<\/code> is responsible for converting the information to a stream of 1s and 0s. <code>FileStream<\/code> and <code>File<\/code> are used to create a save file with the <em>dat<\/em> extension under the application&#8217;s \u201cpersistent data path\u201d followed by any remaining path you wish to make. The persistent data path is <em>C:\\Users\\[user]\\AppData\\LocalLow\\[company name]<\/em>.<\/p>\n<p>A new instance of <code>SaveData<\/code> is created, and the variables within <code>SaveData<\/code> are given the variables in <code>SaveSerial<\/code>. The <code>BinaryFormatter<\/code> serializes that data to the file defined in the <code>FileStream<\/code>. The file is then closed, and a message is printed to the debug console saying the data was saved. Like before, the <code>LoadGame<\/code> method is very much the same but in reverse.<\/p>\n<pre class=\"lang:c# theme:vs2012\">void LoadGame()\r\n{\r\n\tif (File.Exists(Application.persistentDataPath \r\n                   + \"\/MySaveData.dat\"))\r\n\t{\r\n\t\tBinaryFormatter bf = new BinaryFormatter();\r\n\t\tFileStream file = \r\n                   File.Open(Application.persistentDataPath \r\n                   + \"\/MySaveData.dat\", FileMode.Open);\r\n\t\tSaveData data = (SaveData)bf.Deserialize(file);\r\n\t\tfile.Close();\r\n\t\tintToSave = data.savedInt;\r\n\t\tfloatToSave = data.savedFloat;\r\n\t\tboolToSave = data.savedBool;\r\n\t\tDebug.Log(\"Game data loaded!\");\r\n\t}\r\n\telse\r\n\t\tDebug.LogError(\"There is no save data!\");\r\n}<\/pre>\n<p>Your save file by the name of <em>MySaveData.dat <\/em>is searched for in the same path given in the <code>SaveGame<\/code> method. Assuming it&#8217;s found, it will open the file and deserialize it using <code>BinaryFormatter<\/code>. Then the variables found in the save file will be fed into <code>SaveSerial's<\/code> variables. At the end, a message is printed to the debug console saying the load was performed successfully. If there is no file found at the file path, an error message will display instead.<\/p>\n<p>Finally, there&#8217;s the act of deleting and resetting save data. This is extremely similar to the <code>PlayerPrefs<\/code> method but with a couple of extra steps. Unity will first check to make sure there&#8217;s a file at the save location before attempting any deleting of files. Assuming there is a file to delete, the variables in this script will also be reset to some default values and a message printed to the console. Like within the <code>LoadGame<\/code> method, an error message will be printed to the console if there is no file to be found.<\/p>\n<pre class=\"lang:c# theme:vs2012 \">void ResetData()\r\n{\r\n\tif (File.Exists(Application.persistentDataPath \r\n                  + \"\/MySaveData.dat\"))\r\n\t{\r\n\t\tFile.Delete(Application.persistentDataPath \r\n                          + \"\/MySaveData.dat\");\r\n\t\tintToSave = 0;\r\n\t\tfloatToSave = 0.0f;\r\n\t\tboolToSave = false;\r\n\t\tDebug.Log(\"Data reset complete!\");\r\n\t}\r\n\telse\r\n\t\tDebug.LogError(\"No save data to delete.\");\r\n}<\/pre>\n<p>This concludes this script showcasing saving via serialization. Once again, save the code and go back to Unity. Attach <em>SaveSerial <\/em>to the same object as before and disable the <em>SavePrefs <\/em>script component.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"237\" height=\"271\" class=\"wp-image-86243\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/02\/word-image-6.jpeg\" \/><\/p>\n<p class=\"caption\">Figure 7: Disabling the <em>Save Prefs <\/em>component.<\/p>\n<p>When you run the game, the same UI from earlier appears with some alterations. Tinker with the variables like last time and try saving the game. This time a file is saved to the \u201cpersistent data path\u201d of the game, which can be found at <em>C:\\Users\\username\\AppData\\LocalLow\\project name <\/em>on Windows and <em>~\/Library\/Application Support\/companyname\/productname <\/em>on Mac, according to the Unity documentation. Close and reopen the game, then click the load button to bring those values back into the game. And of course, you can delete the saved data entirely if you so desire.<\/p>\n<p class=\"caption\"><img loading=\"lazy\" decoding=\"async\" width=\"885\" height=\"413\" class=\"wp-image-86244\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2020\/02\/word-image-7.jpeg\" \/> Figure 8: The project in action, using serialization<\/p>\n<h2>Conclusion<\/h2>\n<p>Barring some very specific exceptions, such as games designed around short play sessions or \u201cperma-death\u201d (perma-death is a game mechanic where once the player loses the game they have to start completely over and everything resets), saving data will be crucial for user retention. Even in those aforementioned examples, most games will at least save a high score or an achievement. What data is saved and how you save that data is down to you and your project&#8217;s needs.<\/p>\n<p>Though less secure and limited in what it can save, <em>PlayerPrefs <\/em>can be helpful for saving a player&#8217;s in-game preferences or for games where it doesn&#8217;t matter much if the user tinkers with the variables outside the game. In addition, it&#8217;s very simple to use which can help save some development time. Meanwhile, serializing data to a file is more complicated but in return, you can save many other types of data and have more security. Like many things in game development, the tools available to you can be utilized in a variety of ways. However, you choose to save your game&#8217;s data, there will be options available to you. In the end, the best method is the one that helps you and your project the most.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Two approaches to saving game data in Unity &#8211; PlayerPrefs for simple key-value persistence of primitives (int, float, string), and C# serialization for saving complex object graphs including inventories, player state, and scene data. With code examples and guidance on when to use each.&hellip;<\/p>\n","protected":false},"author":317499,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538,53],"tags":[5134],"coauthors":[52549],"class_list":["post-86236","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","category-featured","tag-sql-prompt"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/86236","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/317499"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=86236"}],"version-history":[{"count":6,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/86236\/revisions"}],"predecessor-version":[{"id":86246,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/86236\/revisions\/86246"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=86236"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=86236"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=86236"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=86236"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}