1

Given a variant style class in 3rd party library coded like this:

enum VariantType
{
   Type1,
   Type2,
   ... etc
   TypeN
}

class Variant
{
    [SerializeField]
    private VariantType _variantType;
    public VariantType VariantType => _variantType;


    [SerializeField] // Unity Serialization attribute
    private Type1 _type1;
    public Type1 Type1 => _type1;

    ... etc

    [SerializeField] // Unity Serialization attribute
    private TypeN _typeN;
    public TypeN TypeN => _typeN;
}

Is there a way using JSON.Net to only serialize the single property identified by the VariantType when serializing Variant?

So we get

{
   "_variantType": "Type1",
   "_type1": {...}
}

and not (given that none of the properties are null)

{
   "_variantType": "Type1",
   "_type1": {...}
...
   "_typeN": {...}
}

I've investigated using a custom ContractResolver and CreateProperty, but there doesn't seem to be a way of accessing the parent object.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Phil
  • 42,255
  • 9
  • 100
  • 100
  • You could use the [`ShouldSerialize`](https://www.newtonsoft.com/json/help/html/conditionalproperties.htm#ShouldSerialize) pattern for each property to be serialized conditionally. See: [How to Ignoring Fields and Properties Conditionally During Serialization Using JSON.Net?](https://stackoverflow.com/q/34304738/3744182) and [Making a property deserialize but not serialize with json.net](https://stackoverflow.com/q/11564091/3744182). – dbc Feb 23 '22 at 04:46
  • I can't apply attributes and contract resolver doesn't allow access to the parent object, but maybe using a JSON converter will do the trick. – Phil Feb 23 '22 at 08:17
  • When you say *I can't apply attributes* do you mean that you cannot apply Json.NET attributes, or you cannot change the type `Variant` in any way, e.g. by adding `ShouldSerialize` methods? – dbc Feb 23 '22 at 14:32
  • 1
    Exactly. It's a 3rd party Unity library. I should also have mentioned the fields are being serialized not the properties. – Phil Feb 23 '22 at 16:16
  • You should add that to the question itself, these are important constraints. – Lasse V. Karlsen Feb 25 '22 at 13:05

2 Answers2

0

With Linq 2 JSON you can do the following:

//Parse your json as JObject
var json = "{\"_variantType\": \"Type1\",\"_type1\": {},\"_type2\": {},\"_typeN\": {}}";
var semiParsedJson = JObject.Parse(json);

//Retrieve the variant type
var variantTypeNode = semiParsedJson["_variantType"];
var variantType = variantTypeNode.ToString();

//Construct the type name from the variant type then retrieve it
var typeNodeName = $"_{char.ToLower(variantType[0])}{variantType.Substring(1)}";
var typeNode = semiParsedJson[typeNodeName];

//Create a new object to add there the retrieved tokens' parents
var rootObject = new JObject();
rootObject.Add(variantTypeNode.Parent);
rootObject.Add(typeNode.Parent);

Calling the ToString on the rootObject will emit this

{
  "_variantType": "Type1",
  "_type1": {}
}
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
0

It turns out I was wrong and it is possible to access the parent object from a ContractResolver. The answer for me was to extend the ContractResolver I already had. See https://www.newtonsoft.com/json/help/html/ContractResolver.htm

        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            JsonProperty property = base.CreateProperty(member, memberSerialization);
            if (property.DeclaringType == typeof(Variant))
            {
                property.ShouldSerialize =
                    instance =>
                    {
                        Variant e = (Variant)instance;

                        switch (property.PropertyName)
                        {
                            case "_type1":
                                return e.Type == VariantType.Type1;
 ... etc

Phil
  • 42,255
  • 9
  • 100
  • 100