1

I have some JSON that looks like this:

{
    "fs_Unique_Form_Name": {
        "title": "Title Text",
        "description": "This is the description.",
        "code": "123"
    },
    "stackId": 1,
    "stateId": 1,
    "timeStamp": "2020-11-04:10.30.48"
} 

I am using Newtonsoft.Json to try and deserialize it to a model object, but am having a hard time with the dynamic keys; I'd like to make that property generic if possible since the inner object is always the same.

Here are the models I am trying to use:

public class FormDetail {
    [JsonProperty(PropertyName = "title")]
    public string Title { get; set; }

    [JsonProperty(PropertyName = "description")]
    public string Description { get; set; }

    [JsonProperty(PropertyName = "code")]
    public string Code { get; set; }
}

public class FormResponse {
    [JsonProperty(PropertyName = "NEED TO FIGURE THIS OUT")]
    public FormDetail FormDetail { get; set; }

    [JsonProperty(PropertyName = "stackId")]
    public int StackId { get; set; }

    [JsonProperty(PropertyName = "stateId")]
    public int StateId { get; set; }

    [JsonProperty(PropertyName = "timeStamp")]
    public string TimeStamp { get; set; }
}

I would like to get the whole JSON deserialized into the FormResponse object, but am having difficulty because the fs_Unique_Form_Name key is dynamic after the fs_ portion, but the keys (title, description, code) in that object are static.

Is there a way for me to do something where I can deserialize it to the FormDetail property when the JSON key starts with fs_?

2 Answers 2

2

You can do this by making a custom JsonConverter for your FormResponse class. Below is the code you would need for the converter:

public class FormResponseConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(FormResponse);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load the JSON into a JObject so we can inspect it.
        JObject obj = JObject.Load(reader);

        // Populate all the known properties on a new instance of FormResponse.
        FormResponse response = new FormResponse();
        serializer.Populate(obj.CreateReader(), response);

        // Now find the first property in the JObject whose name starts with "fs_".
        // If there is one, use it to populate the FormDetail on the response.
        JProperty prop = obj.Properties().FirstOrDefault(p => p.Name.StartsWith("fs_"));
        response.FormDetail = prop != null ? prop.Value.ToObject<FormDetail>(serializer) : null;

        return response;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

To use the converter, add a [JsonConverter] attribute to your FormResponse class as shown below. You can remove the [JsonProperty] attribute from the FormDetail property as it is not needed.

[JsonConverter(typeof(FormResponseConverter))]
public class FormResponse
{
    public FormDetail FormDetail { get; set; }

    [JsonProperty(PropertyName = "stackId")]
    public int StackId { get; set; }

    [JsonProperty(PropertyName = "stateId")]
    public int StateId { get; set; }

    [JsonProperty(PropertyName = "timeStamp")]
    public string TimeStamp { get; set; }
}

Then you can deserialize as you normally would:

var response = JsonConvert.DeserializeObject<FormResponse>(json);

Here is a working demo: https://dotnetfiddle.net/jHyRcK

Sign up to request clarification or add additional context in comments.

Comments

1

There are many ways to do it.

For example, let's create a custom reader.

class CustomJsonReader : JsonTextReader
{
    public CustomJsonReader(TextReader reader) : base(reader) { }

    public override object Value
    {
        get
        {
            if (base.TokenType == JsonToken.PropertyName &&
                base.Value.ToString().StartsWith("fs_"))
                return "FormDetail";

            return base.Value;
        }
    }
}

Use it like this

FormResponse response;
var serializer = JsonSerializer.CreateDefault();

using (var streamReader = new StreamReader("test.json"))
using (var jsonReader = new CustomJsonReader(streamReader))
{
    response = serializer.Deserialize<FormResponse>(jsonReader);
}

In this case, the FormDetail property does not need the JsonProperty attribute.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.