diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs
new file mode 100644
index 0000000000..f2e9cb174a
--- /dev/null
+++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs
@@ -0,0 +1,112 @@
+#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_6000_0_OR_NEWER
+
+using System;
+using NUnit.Framework;
+using System.Collections;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.InputSystem;
+using UnityEngine.InputSystem.Editor;
+using UnityEngine.TestTools;
+using UnityEngine.UIElements;
+
+internal enum SomeEnum
+{
+    OptionA = 10,
+    OptionB = 20
+}
+
+#if UNITY_EDITOR
+[InitializeOnLoad]
+#endif
+internal class CustomProcessor : InputProcessor<float>
+{
+    public SomeEnum SomeEnum;
+
+#if UNITY_EDITOR
+    static CustomProcessor()
+    {
+        Initialize();
+    }
+
+#endif
+
+    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
+    private static void Initialize()
+    {
+        InputSystem.RegisterProcessor<CustomProcessor>();
+    }
+
+    public override float Process(float value, InputControl control)
+    {
+        return value;
+    }
+}
+
+internal class CustomProcessorEnumTest : UIToolkitBaseTestWindow<InputActionsEditorWindow>
+{
+    InputActionAsset m_Asset;
+
+    public override void OneTimeSetUp()
+    {
+        base.OneTimeSetUp();
+        m_Asset = AssetDatabaseUtils.CreateAsset<InputActionAsset>();
+
+        var actionMap = m_Asset.AddActionMap("Action Map");
+
+        actionMap.AddAction("Action", InputActionType.Value, processors: "Custom(SomeEnum=10)");
+    }
+
+    public override void OneTimeTearDown()
+    {
+        AssetDatabaseUtils.Restore();
+        base.OneTimeTearDown();
+    }
+
+    public override IEnumerator UnitySetup()
+    {
+        m_Window = InputActionsEditorWindow.OpenEditor(m_Asset);
+        yield return null;
+    }
+
+    [UnityTest]
+    public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset()
+    {
+        // Serialize current asset to JSON, and check that initial JSON contains default enum value for OptionA
+        var json = m_Window.currentAssetInEditor.ToJson();
+
+        Assert.That(json.Contains("Custom(SomeEnum=10)"), Is.True,
+            "Serialized JSON does not contain the expected custom processor string for OptionA.");
+
+        // Query the dropdown with exactly two enum choices and check that the drop down is present in the UI
+        var dropdownList = m_Window.rootVisualElement.Query<DropdownField>().Where(d => d.choices.Count == 2).ToList();
+        Assume.That(dropdownList.Count > 0, Is.True, "Enum parameter dropdown not found in the UI.");
+
+        // Determine the new value to be set in the dropdown, focus the dropdown before dispatching the change
+        var dropdown = dropdownList.First();
+        var newValue = dropdown.choices[1];
+        dropdown.Focus();
+        dropdown.value = newValue;
+
+        // Create and send a change event from OptionA to OptionB
+        var changeEvent = ChangeEvent<Enum>.GetPooled(SomeEnum.OptionA, SomeEnum.OptionB);
+        changeEvent.target = dropdown;
+        dropdown.SendEvent(changeEvent);
+
+        // Find the save button in the window, focus and click the save button to persist the changes
+        var saveButton = m_Window.rootVisualElement.Q<Button>("save-asset-toolbar-button");
+        Assume.That(saveButton, Is.Not.Null, "Save Asset button not found in the UI.");
+        saveButton.Focus();
+        SimulateClickOn(saveButton);
+
+        Assert.That(dropdown.value, Is.EqualTo(newValue));
+
+        // Verify that the updated JSON contains the new enum value for OpitonB
+        var updatedJson = m_Window.currentAssetInEditor.ToJson();
+        Assert.That(updatedJson.Contains("Custom(SomeEnum=20)"), Is.True, "Serialized JSON does not contain the updated custom processor string for OptionB.");
+
+        yield return null;
+    }
+}
+#endif
diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs.meta b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs.meta
new file mode 100644
index 0000000000..bb391f4d03
--- /dev/null
+++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 2ad69ed3d3bf6e343939dbdf0d95803b
\ No newline at end of file
diff --git a/Assets/Tests/InputSystem/CoreTests_Actions.cs b/Assets/Tests/InputSystem/CoreTests_Actions.cs
index 37be511d5b..0a4d551fdd 100644
--- a/Assets/Tests/InputSystem/CoreTests_Actions.cs
+++ b/Assets/Tests/InputSystem/CoreTests_Actions.cs
@@ -5215,8 +5215,8 @@ public void Actions_CanConvertAssetToAndFromJson()
     static string MinimalJson(string name = null)
     {
         if (name != null)
-            return "{\n    \"name\": \"" + name + "\",\n    \"maps\": [],\n    \"controlSchemes\": []\n}";
-        return "{\n    \"maps\": [],\n    \"controlSchemes\": []\n}";
+            return "{\n    \"version\": 0,\n    \"name\": \"" + name + "\",\n    \"maps\": [],\n    \"controlSchemes\": []\n}";
+        return "{\n    \"version\": 0,\n    \"maps\": [],\n    \"controlSchemes\": []\n}";
     }
 
     [Test]
diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md
index 6425648904..e9e79a41f1 100644
--- a/Packages/com.unity.inputsystem/CHANGELOG.md
+++ b/Packages/com.unity.inputsystem/CHANGELOG.md
@@ -38,6 +38,7 @@ however, it has to be formatted properly to pass verification tests.
 - Fixed the defaultActionMap dropdown in the PlayerInput component defaulting to <None> instead of the first ActionMap.
 - Fixed TrackedPoseDriver stops updating position and rotation when device is added after its initialization. [ISXB-1555](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1555)
 - Fixed PlayerInput component not working with C# Wrappers (ISXB-1535). This reverted changes done to fix [ISXB-920](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-920) but users can now fix it themselves.
+- Fixed an issue that caused input processors with enum properties to incorrectly serialise by index instead of by value [ISXB-1474](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-1474)
 
 ## [1.14.0] - 2025-03-20
 
diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs
index 7ddd84661e..3b9a70fcca 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs
@@ -1,6 +1,9 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using UnityEngine.InputSystem.Editor;
 using UnityEngine.InputSystem.Utilities;
 
 ////TODO: make the FindAction logic available on any IEnumerable<InputAction> and IInputActionCollection via extension methods
@@ -275,6 +278,21 @@ public InputAction this[string actionNameOrId]
                 return action;
             }
         }
+        /// <summary>
+        /// File‐format version constants for InputActionAsset JSON.
+        /// </summary>
+        static class JsonVersion
+        {
+            /// <summary>The original JSON version format for InputActionAsset.</summary>
+            public const int Version0 = 0;
+
+            /// <summary>Updated JSON version format for InputActionAsset.</summary>
+            /// <remarks>Changes representation of parameter values from being serialized by value to being serialized by value.</remarks>
+            public const int Version1 = 1;
+
+            /// <summary>The current version.</summary>
+            public const int Current  = Version1;
+        }
 
         /// <summary>
         /// Return a JSON representation of the asset.
@@ -296,8 +314,10 @@ public InputAction this[string actionNameOrId]
         /// <seealso cref="FromJson"/>
         public string ToJson()
         {
+            var hasContent = m_ActionMaps.LengthSafe() > 0 || m_ControlSchemes.LengthSafe() > 0;
             return JsonUtility.ToJson(new WriteFileJson
             {
+                version = hasContent ? JsonVersion.Current : JsonVersion.Version0,
                 name = name,
                 maps = InputActionMap.WriteFileJson.FromMaps(m_ActionMaps).maps,
                 controlSchemes = InputControlScheme.SchemeJson.ToJson(m_ControlSchemes),
@@ -379,6 +399,7 @@ public void LoadFromJson(string json)
                 throw new ArgumentNullException(nameof(json));
 
             var parsedJson = JsonUtility.FromJson<ReadFileJson>(json);
+            MigrateJson(ref parsedJson);
             parsedJson.ToAsset(this);
         }
 
@@ -950,6 +971,7 @@ private void OnDestroy()
         [Serializable]
         internal struct WriteFileJson
         {
+            public int version;
             public string name;
             public InputActionMap.WriteMapJson[] maps;
             public InputControlScheme.SchemeJson[] controlSchemes;
@@ -965,6 +987,7 @@ internal struct WriteFileJsonNoName
         [Serializable]
         internal struct ReadFileJson
         {
+            public int version;
             public string name;
             public InputActionMap.ReadMapJson[] maps;
             public InputControlScheme.SchemeJson[] controlSchemes;
@@ -981,5 +1004,73 @@ public void ToAsset(InputActionAsset asset)
                         map.m_Asset = asset;
             }
         }
+
+        /// <summary>
+        /// If parsedJson.version is older than Current, rewrite every
+        /// action.processors entry to replace “enumName(Ordinal=…)” with
+        /// “enumName(Value=…)” and bump parsedJson.version.
+        /// </summary>
+        internal void MigrateJson(ref ReadFileJson parsedJson)
+        {
+            if (parsedJson.version >= JsonVersion.Version1)
+                return;
+            if ((parsedJson.maps?.Length ?? 0) > 0 && (parsedJson.version) < JsonVersion.Version1)
+            {
+                for (var mi = 0; mi < parsedJson.maps.Length; ++mi)
+                {
+                    var mapJson = parsedJson.maps[mi];
+                    for (var ai = 0; ai < mapJson.actions.Length; ++ai)
+                    {
+                        var actionJson = mapJson.actions[ai];
+                        var raw = actionJson.processors;
+                        if (string.IsNullOrEmpty(raw))
+                            continue;
+
+                        var list = NameAndParameters.ParseMultiple(raw).ToList();
+                        var rebuilt = new List<string>(list.Count);
+                        foreach (var nap in list)
+                        {
+                            var procType = InputSystem.TryGetProcessor(nap.name);
+                            if (nap.parameters.Count == 0 || procType == null)
+                            {
+                                rebuilt.Add(nap.ToString());
+                                continue;
+                            }
+
+                            var dict = nap.parameters.ToDictionary(p => p.name, p => p.value.ToString());
+                            var anyChanged = false;
+                            foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => f.FieldType.IsEnum))
+                            {
+                                if (dict.TryGetValue(field.Name, out var ordS) && int.TryParse(ordS, out var ord))
+                                {
+                                    var values = Enum.GetValues(field.FieldType).Cast<object>().ToArray();
+                                    if (ord >= 0 && ord < values.Length)
+                                    {
+                                        dict[field.Name] = Convert.ToInt32(values[ord]).ToString();
+                                        anyChanged = true;
+                                    }
+                                }
+                            }
+
+                            if (!anyChanged)
+                            {
+                                rebuilt.Add(nap.ToString());
+                            }
+                            else
+                            {
+                                var paramText = string.Join(",", dict.Select(kv => $"{kv.Key}={kv.Value}"));
+                                rebuilt.Add($"{nap.name}({paramText})");
+                            }
+                        }
+
+                        actionJson.processors = string.Join(";", rebuilt);
+                        mapJson.actions[ai] = actionJson;
+                    }
+                    parsedJson.maps[mi] = mapJson;
+                }
+            }
+            // Bump the version so we never re-migrate
+            parsedJson.version = JsonVersion.Version1;
+        }
     }
 }
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/ParameterListView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/ParameterListView.cs
index 4e1df4bb84..2828d7654a 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/ParameterListView.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/ParameterListView.cs
@@ -278,11 +278,26 @@ void OnEditEnd()
 
                 if (parameter.isEnum)
                 {
-                    var intValue = parameter.value.value.ToInt32();
-                    var field = new DropdownField(label.text, parameter.enumNames.Select(x => x.text).ToList(), intValue);
-                    field.tooltip = label.tooltip;
-                    field.RegisterValueChangedCallback(evt => OnValueChanged(ref parameter, field.index, closedIndex));
-                    field.RegisterCallback<BlurEvent>(_ => OnEditEnd());
+                    var names = parameter.enumNames.Select(c => c.text).ToList();
+                    var rawValue = parameter.value.value.ToInt32();
+                    var selectedIndex = parameter.enumValues.IndexOf(rawValue);
+                    if (selectedIndex < 0 || selectedIndex >= names.Count)
+                        selectedIndex = 0;
+
+                    var field = new DropdownField(label.text, names, selectedIndex)
+                    {
+                        tooltip = label.tooltip
+                    };
+
+                    field.RegisterValueChangedCallback(evt =>
+                    {
+                        var newBackingValue = parameter.enumValues[field.index];
+                        parameter.value.value = PrimitiveValue.FromObject(newBackingValue).ConvertTo(parameter.value.type);
+                        m_Parameters[closedIndex] = parameter;
+                        onChange?.Invoke();
+                    });
+
+                    field.RegisterCallback<BlurEvent>(_ => onChange?.Invoke());
                     root.Add(field);
                 }
                 else if (parameter.value.type == TypeCode.Int64 || parameter.value.type == TypeCode.UInt64)
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporter.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporter.cs
index 03910a9d1a..ddc2ef6430 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporter.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporter.cs
@@ -28,7 +28,7 @@ namespace UnityEngine.InputSystem.Editor
     [ScriptedImporter(kVersion, InputActionAsset.Extension)]
     internal class InputActionImporter : ScriptedImporter
     {
-        private const int kVersion = 13;
+        private const int kVersion = 14;
 
         [SerializeField] private bool m_GenerateWrapperCode;
         [SerializeField] private string m_WrapperCodePath;
@@ -66,7 +66,6 @@ private static InputActionAsset CreateFromJson(AssetImportContext context)
             {
                 // Attempt to parse JSON
                 asset.LoadFromJson(content);
-
                 // Make sure action map names are unique within JSON file
                 var names = new HashSet<string>();
                 foreach (var map in asset.actionMaps)
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.cs
index 9206101d9c..6c8dea7ae7 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.cs
@@ -42,6 +42,7 @@ public class DefaultInputActions : IInputActionCollection2, IDisposable
         public @DefaultInputActions()
         {
             asset = InputActionAsset.FromJson(@"{
+    ""version"": 1,
     ""name"": ""DefaultInputActions"",
     ""maps"": [
         {
diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.inputactions b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.inputactions
index 6fa20869f1..45f6cc2b89 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.inputactions
+++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/DefaultInputActions.inputactions
@@ -1,4 +1,5 @@
 {
+    "version": 1,
     "name": "DefaultInputActions",
     "maps": [
         {