Version: Unity 6.5 Alpha (6000.5)
LanguageEnglish
  • C#

SerializedProperty.Parent

Suggest a change

Success!

Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable.

Close

Submission failed

For some reason your suggested change could not be submitted. Please <a>try again</a> in a few minutes. And thank you for taking the time to help us improve the quality of Unity Documentation.

Close

Cancel

Declaration

public bool Parent();

Description

Moves to the parent property.

Updates the SerializedProperty so that it points to its parent property in the hierarchy.

This method navigates upward in the property tree, which reverses a previous Next call with enterChildren set to true. Use it to return to a parent property after you explore its child properties.

If the property is already at the root level, this method returns false and leaves the property state unchanged.

Behavior with object modifications

Parent() continues to work correctly after modifications to the serialized object, whether the modifications are made:
- Directly on the object followed by SerializedObject.Update.
- Via SerializedProperty followed by SerializedObject.ApplyModifiedProperties.

The method automatically handles cases in which:
- Modifications occur before the current property position (such as resizing an array above the property) - the property is automatically refreshed.
- Modifications occur after the current property position (such as resizing an array below the property) - the property remains valid.

However, if the property itself becomes invalid due to modifications (such as an array element property after the array is resized to exclude that element), Parent() throws an ObjectDisposedException.

Parent() works correctly when called from array element properties and navigates up through the array structure (element → Array container → array property → parent).

using System;
using System.Text;
using UnityEngine;
using UnityEditor;

public class SerializedPropertyParentExample : ScriptableObject
{
    [System.Serializable]
    public struct NestedData
    {
        public int x;
        public int y;
        public Vector2[] dynamicArray;
        public Vector2 position;
    }
    public NestedData m_data;
    public bool m_otherField;

    [MenuItem("Example/SerializedProperty Parent Example")]
    static void ParentExample()
    {
        var scriptableObject = ScriptableObject.CreateInstance<SerializedPropertyParentExample>();
        scriptableObject.m_data.x = 10;
        scriptableObject.m_data.y = 20;
        scriptableObject.m_data.dynamicArray = new Vector2[]
        {
            new Vector2(1, 2),
            new Vector2(3, 4),
            new Vector2(5, 6)
        };
        scriptableObject.m_data.position = new Vector2(100, 200);

        using (var serializedObject = new SerializedObject(scriptableObject))
        {
            var sb = new StringBuilder();

            // Example 1: Basic Parent navigation.
            sb.AppendLine("=== Example 1: Basic Parent Navigation ===");
            var property = serializedObject.FindProperty("m_data.position.y");
            sb.AppendLine($"Starting at: {property.propertyPath} (depth {property.depth})");

            // Navigate up the hierarchy.
            property.Parent(); // position.
            sb.AppendLine($"After Parent(): {property.propertyPath} (depth {property.depth})");

            property.Parent(); // m_data.
            sb.AppendLine($"After Parent(): {property.propertyPath} (depth {property.depth})");

            bool canGoUp = property.Parent(); // Try to go beyond root.
            sb.AppendLine($"Can navigate further up: {canGoUp}");

            // Example 2: Parent works after modifications when property is before the change.
            sb.AppendLine("\n=== Example 2: Parent After Modification (Property Before Change) ===");
            property = serializedObject.FindProperty("m_data.position.y");
            sb.AppendLine($"Property at: {property.propertyPath}");

            // Modify the object directly and update SerializedObject.
            scriptableObject.m_data.dynamicArray = new Vector2[] { new Vector2(1, 2) }; // Resize array above position.
            serializedObject.Update();

            // Parent still works because the property is after the modification point.
            property.Parent();
            sb.AppendLine($"After resize and Parent(): {property.propertyPath} (still valid)");
            sb.AppendLine($"Position value: ({property.vector2Value.x}, {property.vector2Value.y})");

            // Example 3: Parent works after SerializedObject modifications.
            sb.AppendLine("\n=== Example 3: Parent After SerializedObject Modification ===");
            serializedObject.Update(); // Refresh to get latest state.
            property = serializedObject.FindProperty("m_data.position.y");
            sb.AppendLine($"Property at: {property.propertyPath}");

            // Modify via SerializedProperty and apply.
            var arrayProp = serializedObject.FindProperty("m_data.dynamicArray");
            arrayProp.arraySize = 2;
            serializedObject.ApplyModifiedProperties();

            // Parent still works.
            property.Parent();
            sb.AppendLine($"After ApplyModifiedProperties and Parent(): {property.propertyPath} (still valid)");

            // Example 4: Parent navigation from array element.
            sb.AppendLine("\n=== Example 4: Parent From Array Element ===");
            serializedObject.Update();
            property = serializedObject.FindProperty("m_data.dynamicArray.Array.data[2].y");
            sb.AppendLine($"Starting at: {property.propertyPath} (depth {property.depth})");

            // Navigate up through array structure.
            property.Parent(); // data[2].
            sb.AppendLine($"After Parent(): {property.propertyPath} (Vector2 element)");

            property.Parent(); // Array.
            sb.AppendLine($"After Parent(): {property.propertyPath} (array container)");

            property.Parent(); // dynamicArray.
            sb.AppendLine($"After Parent(): {property.propertyPath} (array property)");

            property.Parent(); // m_data.
            sb.AppendLine($"After Parent(): {property.propertyPath} (parent struct)");

            // Example 5: Parent from array element after modifications.
            sb.AppendLine("\n=== Example 5: Parent From Array Element After Modification ===");
            serializedObject.Update();
            property = serializedObject.FindProperty("m_data.dynamicArray.Array.data[2].y");
            sb.AppendLine($"Property at: {property.propertyPath}");

            // Modify another field and update.
            scriptableObject.m_data.x = 999;
            serializedObject.Update();

            // Parent still works from array element.
            property.Parent();
            sb.AppendLine($"After modification and Parent(): {property.propertyPath}");
            sb.AppendLine($"Element value: ({property.vector2Value.x}, {property.vector2Value.y})");

            // Example 6: Parent fails when array element becomes invalid.
            sb.AppendLine("\n=== Example 6: Parent When Array Element Becomes Invalid ===");
            serializedObject.Update();
            property = serializedObject.FindProperty("m_data.dynamicArray.Array.data[2].y");
            sb.AppendLine($"Property at: {property.propertyPath} (array element)");

            // Resize array to invalidate the element.
            scriptableObject.m_data.dynamicArray = new Vector2[] { new Vector2(1, 2) }; // Only 2 elements, index 2 invalid
            serializedObject.Update();

            try
            {
                property.Parent(); // This will throw ObjectDisposedException.
                sb.AppendLine("Parent succeeded (unexpected)");
            }
            catch (ObjectDisposedException)
            {
                sb.AppendLine("Parent threw ObjectDisposedException (expected - property is invalid)");
            }

            /*Expected output:
            === Example 1: Basic Parent Navigation ===
            Starting at: m_data.position.y (depth 2)
            After Parent(): m_data.position (depth 1)
            After Parent(): m_data (depth 0)
            Can navigate further up: False

            === Example 2: Parent After Modification (Property Before Change) ===
            Property at: m_data.position.y
            After resize and Parent(): m_data.position (still valid)
            Position value: (100, 200)

            === Example 3: Parent After SerializedObject Modification ===
            Property at: m_data.position.y
            After ApplyModifiedProperties and Parent(): m_data.position (still valid)

            === Example 4: Parent From Array Element ===
            Starting at: m_data.dynamicArray.Array.data[2].y (depth 2)
            After Parent(): m_data.dynamicArray.Array.data[2] (Vector2 element)
            After Parent(): m_data.dynamicArray.Array (array container)
            After Parent(): m_data.dynamicArray (array property)
            After Parent(): m_data (parent struct)

            === Example 5: Parent From Array Element After Modification ===
            Property at: m_data.dynamicArray.Array.data[2].y
            After modification and Parent(): m_data.dynamicArray.Array.data[2]
            Element value: (5, 6)

            === Example 6: Parent When Array Element Becomes Invalid ===
            Property at: m_data.dynamicArray.Array.data[2].y (array element)
            Parent threw ObjectDisposedException (expected - property is invalid)
            */

            Debug.Log(sb.ToString());
        }
    }
}