https://stackoverflow.com/questions/76987746/how-would-i-create-a-custom-attribute-that-uses-another

CSharp · Console

/Program.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Reflection;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;	

using NUnit.Framework; // Throws NUnit.Framework.AssertionException

//https://stackoverflow.com/questions/76987746/how-would-i-create-a-custom-attribute-that-uses-another

#if false
There is no way to do what you want out of the box with Json.NET because

 - As noted by Marc Gravell, [JsonPropertyAttribute](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonPropertyAttribute.cs#L35) is sealed so you cannot make your attribute inherit from it.
	
 - The trick from [this answer](https://stackoverflow.com/a/38504552) by [Reza Aghaei](https://stackoverflow.com/a/38504552) to [Combining multiple Attributes to a single Attribute - Merge Attributes](https://stackoverflow.com/q/38503146) will not work because Json.NET uses use reflection instead of type description to gather metadata.
								
As a workaround, you could create a [custom contract resolver](https://www.newtonsoft.com/json/help/html/contractresolver.htm#CustomIContractResolverExamples) that adds all fields marked with your `SerializedFieldAttribute` into the serialization contract.
#endif

	public class MyClass
	{
		[SerializedField]
		private int colliderType = 0;

		[SerializedField]
		[JsonIgnore]
		private int ignoreMe = 0;
		
		public void SetColliderType(int type) => colliderType = type;
	}

	public class IncludeSerializedFieldsResolver : DefaultContractResolver
	{
		// TODO: Check the interaction of [SerializedField] with memberSerialization = MemberSerialization.OptIn and MemberSerialization.Fields.
		// TODO: Decide whether fields marked with [SerializedField] and [JsonIgnore] should be serialized.  Currently they are not.
		// TODO: Decide the interaction between [SerializedField] and data contract attributes -- https://www.newtonsoft.com/json/help/html/DataContractAndDataMember.htm
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            var property = base.CreateProperty(member, memberSerialization);
			if (member is FieldInfo f && !f.IsPublic 
				&& Attribute.IsDefined(f, typeof(SerializedFieldAttribute)) 
				&& !Attribute.IsDefined(f, typeof(JsonIgnoreAttribute)))
			{
				property.Readable = property.Writable = true;
			}
            return property;
        }

        protected override List<MemberInfo> GetSerializableMembers(Type objectType)
        {
            var members = base.GetSerializableMembers(objectType);
			var initialCount = members.Count;
			var toAdd = objectType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
				.Where(f => Attribute.IsDefined(f, typeof(SerializedFieldAttribute)) 
					   && !Attribute.IsDefined(f, typeof(JsonIgnoreAttribute)))
				.Where(f => members.IndexOf(f, 0, initialCount) < 0); // Don't add private members that already got added for some other reason, e.g. being marked with [JsonProperty]
			members.AddRange(toAdd);
            return members;
        }
	}

	[System.AttributeUsage(System.AttributeTargets.Field, AllowMultiple = false)]
	public class SerializedFieldAttribute : Attribute { }

    class TestClass
    {
        public static void Test()
        {
			var rootModel = new MyClass();
			rootModel.SetColliderType(22);
			
			// See https://www.newtonsoft.com/json/help/html/performance.htm#ReuseContractResolver
			// For best performance you should cache and reuse this statically.
			IContractResolver resolver = new IncludeSerializedFieldsResolver {  
				// Modify any properties as required, e.g.
				// NamingStrategy = new CamelCaseNamingStrategy(),
			};
			
			var settings = new JsonSerializerSettings {
				ContractResolver = resolver,
				// Add other settings as required.
			};
			var json = JsonConvert.SerializeObject(rootModel, settings);
			Console.WriteLine(json);
			Assert.AreEqual(json, """{"colliderType":22}""");
			var myClass2 = JsonConvert.DeserializeObject<MyClass>(json, settings);
			var json2 = JsonConvert.SerializeObject(myClass2, settings);
			Assert.AreEqual(json2, """{"colliderType":22}""");
		}	
	}

	public class Program
	{
		public static void Main()
		{
			Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
			Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Namespace, typeof(JsonSerializer).Assembly.FullName);
			Console.WriteLine();

			try
			{
				TestClass.Test();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Failed with unhandled exception: ");
				Console.WriteLine(ex);
				throw;
			}
		}
	}
            //Console.WriteLine("Created property {0}, Ignored={1}, Readable={2}, Writable={3}", property, property.Ignored, property.Readable, property.Writable);
            //Console.WriteLine("Returned members: {0}", string.Join(",", members.Select(m => m.Name).ToArray()));
;