2

I am trying to deserialize JSON using the Json.NET library. JSON which I receive looks like:

{
    "responseHeader": {
        "zkConnected": true,
        "status": 0,
        "QTime": 2
    },
    "suggest": {
        "mySuggester": {
            "Ext": {
                "numFound": 10,
                "suggestions": [
                    {
                        "term": "Extra Community",
                        "weight": 127,
                        "payload": ""
                    },
                    {
                        "term": "External Video block",
                        "weight": 40,
                        "payload": ""
                    },
                    {
                        "term": "Migrate Extra",
                        "weight": 9,
                        "payload": ""
                    }
                ]
            }
        }
    }
}

The problem is that the "Ext" that you can see in it is part of the parameter passed in the query string and will always be different. I want to get only the values assigned to the term "term".

I tried something like this, but unfortunately does not works:

public class AutocompleteResultsInfo
{
    public AutocompleteResultsInfo()
    {
        this.Suggest = new Suggest();
    }
    [JsonProperty("suggest")]
    public Suggest Suggest { get; set; }
}

public class Suggest
{
    [JsonProperty("mySuggester")]
    public MySuggesterElement MySuggesterElement { get; set; }
}

public struct MySuggesterElement
{
    public MySuggester MySuggester;
    public string JsonString;

    public static implicit operator MySuggesterElement(MySuggester MySuggester) =>new MySuggesterElement { MySuggester = MySuggester };
    public static implicit operator MySuggesterElement(string String) => new MySuggesterElement { JsonString = String };
}

public class MySuggester
{
    [JsonProperty("suggestions")]
    public Suggestions[] Suggestions { get; set; }
}

public class Suggestions
{
    [JsonProperty("term")]
    public string Autocopmplete { get; set; }
}

internal class SuggesterElementConverter : JsonConverter
{
    public override bool CanConvert(Type t)
    {
        return t == typeof(MySuggesterElement) || t == typeof(MySuggesterElement?);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        switch (reader.TokenType)
        {
            case JsonToken.String:
            case JsonToken.Date:
                var stringValue = serializer.Deserialize<string>(reader);
                return new MySuggesterElement { JsonString = stringValue };
            case JsonToken.StartObject:
                var objectValue = serializer.Deserialize<MySuggester>(reader);
                return new MySuggesterElement { MySuggester = objectValue };
        }
        throw new Exception("Cannot unmarshal type MySuggesterElement");
    }

    public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
    {
        var value = (MySuggesterElement)untypedValue;
        if (value.JsonString != null)
        {
            serializer.Serialize(writer, value.JsonString);
            return;
        }
        if (value.MySuggester != null)
        {
            serializer.Serialize(writer, value.MySuggester);
            return;
        }
        throw new Exception("Cannot marshal type CollationElements");
    }

    public static readonly SuggesterElementConverter Singleton = new SuggesterElementConverter();
}

public class AutocompleteConverter
{
    public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
    {
        MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
        DateParseHandling = DateParseHandling.None,
        Converters =
        {
            SuggesterElementConverter.Singleton
        }
    };
}

var results = JsonConvert.DeserializeObject<AutocompleteResultsInfo>(resultJson, AutocompleteConverter.Settings);

Many thanks for your help.

Kind Regerds, Wojciech

3 Answers 3

5

You could decode the "mySuggester" as a dictionary: public class Suggest

public class Suggest
{
    [JsonProperty("mySuggester")]
    public Dictionary<string, MySuggester> MySuggester { get; set; }
}

Then you'll be able to access the suggestions with the query string parameter:

var variablePropertyName = "Ext";

var result = JsonConvert.DeserializeObject<AutocompleteResultsInfo>(_json);

var suggestions = result.Suggest.MySuggester[variablePropertyName].Suggestions;

if you don't know the property name you could also look it up in the dictionary:

var variablePropertyName = result.Suggest.MySuggester.Keys.First();

Working example: https://dotnetfiddle.net/GIKwLs

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

Comments

1

If you don't need to deserialize the whole json string you can use a JsonTextReader. Example:

private static IEnumerable<string> GetTerms(string json)
{
    using (JsonTextReader reader = new JsonTextReader(new StringReader(json)))
    {
        while (reader.Read())
        {
            if (reader.TokenType == JsonToken.PropertyName && reader.Value.Equals("term"))
            {
                string term = reader.ReadAsString();

                yield return term;
            }
        }
    }
}

Using the code:

string json = @"{
    ""responseHeader"": {
        ""zkConnected"": true,
        ""status"": 0,
        ""QTime"": 2
    },
    ""suggest"": {
        ""mySuggester"": {
            ""Ext"": {
                ""numFound"": 10,
                ""suggestions"": [
                    {
                        ""term"": ""Extra Community"",
                        ""weight"": 127,
                        ""payload"": """"
                    },
                    {
                        ""term"": ""External Video block"",
                        ""weight"": 40,
                        ""payload"": """"
                    },
                    {
                        ""term"": ""Migrate Extra"",
                        ""weight"": 9,
                        ""payload"": """"
                    }
                ]
            }
        }
    }
}";

IEnumerable<string> terms = GetTerms(json);

foreach (string term in terms)
{
    Console.WriteLine(term);
}

Comments

1

If you only need the object containing the term, and nothing else, you could work with the JSON values directly by using the JObject interface in JSON.Net.

var parsed = JObject.Parse(jsonString);
var usingLinq = (parsed["suggest"]["mySuggester"] as JObject)
    .Descendants()
    .OfType<JObject>()
    .Where(x => x.ContainsKey("term"));

var usingJsonPath = parsed.SelectTokens("$.suggest.mySuggester.*.*[?(@.term)]")
    .Cast<JObject>();

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.