Jerrod Horton
Jerrod Horton

Reputation: 1692

Deserialize $ref document pointer using JSON.Net

I have been up for hours trying to figure this out. There seem to be a lot of posts tip toe'ing around the issue I am having but none of the posts seem to work for me.

I very simply want to deserialize the $ref field in my json document using json.net

Example JSON:

{
  "items": [
    { "$ref": "#/parameters/RequestId" },
    {
      "name": "user",
      "in": "body",
      "description": "User metadata object",
      "required": true
    }
  ],
  "parameters": {
    "RequestId": {
      "name": "X-requestId",
      "in": "header",
      "description": "this is a request id",
      "required": false
    }
  }
}

Example Classes:

public class Item
{
  public string Name {get;set;}
  public string In {get;set;}
  public string Description {get;set;}
  public bool Required {get;set;}
}

public class RootDoc 
{   
  public List<Item> Items {get;set;}   
  public Dictionary<string, Item> Parameters {get;set;}
}

What I am expecting:

var doc = JsonConvert.DeserializeObject<RootDoc>(json, new JsonSerializerSettings()
                {
                    MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
                    PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                    ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
                    Error = delegate (object sender, ErrorEventArgs args)
                    {
                        // Handle all errors
                        args.ErrorContext.Handled = true;
                    }
                });

Assert.IsNotNull(doc);
Assert.IsNotNull(doc.Items);
Assert.IsTrue(doc.Items.Count == 2);
Assert.IsTrue(doc.Items[0].Name == "X-requestId");
Assert.IsTrue(doc.Items[1].Name == "user");

EDIT:

I answered the question below. I can't believe I had to do that but it works.

Upvotes: 2

Views: 1504

Answers (2)

BWA
BWA

Reputation: 5764

You can use this code.

JObject o = JObject.Parse(json);

JToken tokenToReplace = o.SelectToken("$..$ref");
string[] s = tokenToReplace.Value<string>().Split('/');
string a = s[s.Length - 1];
JToken newToken = o.SelectToken("$.." + a);

JArray items = (JArray)o["items"];
items.Add(newToken);
tokenToReplace.Parent.Parent.Remove();

string newJson = o.ToString();

newJson contains replaced $ref.

Upvotes: 0

Jerrod Horton
Jerrod Horton

Reputation: 1692

Wow... So here is what I did to solve the problem. I'm not sure if there is an easier way and I can't believe json.net does not provide this out of the box.

var x = JObject.Parse(json);
            var xx = x.Descendants().ToList().Where(d => d.Path.ToLower().EndsWith("$ref"));
            foreach (var item in xx)
            {
                if (!item.HasValues)
                    continue;
                string str = item.First.ToString().TrimStart(new char[] { '#' }).TrimStart(new char[] { '/' });
                var split = str.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            JToken token = x;
            for (int i = 0;i<split.Length; i++)
            {
                token = token[split[i]];
            }

            item.Parent.Replace(token);
        }

        var doc = JsonConvert.DeserializeObject<RootDoc>(x.ToString(), new JsonSerializerSettings()
        {
            PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
            Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
            {
                // Handle all errors
                args.ErrorContext.Handled = true;
            }
        });

        Assert.IsNotNull(doc);
        Assert.IsNotNull(doc.Items);
        Assert.IsTrue(doc.Items.Count == 2);
        Assert.IsTrue(doc.Items[0].Name == "X-requestId");
        Assert.IsTrue(doc.Items[1].Name == "user");

Upvotes: 2

Related Questions