0

In my project, I use a custom ContractResolver to apply dynamically a custom JSONConverter on specific properties of my object types. I also use a custom ReferenceResolver in order to keep track not only of in-file references but also of external ones (coming from other JSON files).

Since I have a custom JSONConverter, I have to manually check the presence of $ref in the JSON text and call my ReferenceResolver to find the pointed object.

What I am trying to achieve now is to postpone the resolution of certain missing references. Let say we have the file json1.json which have a reference to json2.json file into it, but we didn't deserialize json2.json yet. So a missing reference entry has to be tracked in order to be fixed later.

Here is a small snippet about the handling of missing references.

public override MyObject? ReadJson(JsonReader reader, Type objectType, MyObject? existingValue, bool hasExistingValue, Newtonsoft.Json.JsonSerializer serializer)
{
    switch (reader.TokenType)
    {
         case JsonToken.StartObject:
         {
              var jsonObj = JObject.Load(reader);
              if (jsonObj.ContainsKey("$ref"))
              {
                   var id = (string)jsonObj["$ref"]!;
                   var ret = serializer.ReferenceResolver!.ResolveReference(serializer, id) as MyObject;;
                   if (ret == null)
                   {
                        // unresolved reference
                        var missingObjectId = Guid.Parse(id); // <-- GUID to track
                        var memberPath = reader.Path;         // <-- Current object property path to track
                        var currentObject = ???;              // <-- Object with missing reference to track
                        StoreMissingReference(currentObject, memberPath, missinObjectId);
                   }
                   return ret;
              }
              return jsonObj.ToObject(objectType, serializer) as MyObject;
         }
    }

}

The problem here is to access the currently populated object. What I can see in the call stack is the IValueProvider has a reference of the targetObject to set the value on it. But I am not sure what is the best approach with IValueProvider. Create one from scratch and assign it inside the ContractResolver?

Also right now I use the property member path from the reader for my missing reference, but I will change that and pass the MemberInfo from the ContractResolver to the JSONConverter constructor. It will be easier to set the value later with Reflection.

Thank you!

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
jbltx
  • 1,255
  • 2
  • 19
  • 34
  • Something like [How can I deserialize instances of a type that has read-only back-references to some container type also being deserialized?](https://stackoverflow.com/q/56316631/3744182)? – dbc Mar 28 '21 at 05:36
  • If you don’t have the JSON2 yet it is not a synchronous code anymore, so I doubt you can achieve your reference resolving in one shot. I would suggest you to wait until you have all your data and only then start the deserialising process. – kolodi Mar 28 '21 at 09:06
  • I see what you mean @kolodi, but the purpose of tracking missing references is to be able to deserialize partially my objects and have interruptions between deserialization processes. @dbc thank you for the answer, it seems to be the right direction. Ideally I would want to use the context object in `ReferenceResolver.GetReference` to retrieve the target object, but I am not sure if it's possible. – jbltx Mar 28 '21 at 18:26

0 Answers0