0

I'm new to .NET and MVC. Struggling to deserialize some JSON to an object in the ViewBag. The JSON is coming from Azure Search. I've verified the WebHttpRequest returns JSON like this:

{
    "@odata.context": "https://url.toazuresearch/indexes('index01')/$metadata#docs",
    "value": [
        {
            "@search.score": 0.31507686,
            "Id": "34",
            "Date": "2017-08-24T09:14:56.193Z",
            "Domain": "domain.com",
            "RuleName": "Legacy Reports",
            "Log": "Log text",
            "ChangeId": "changeId",
            "ParentId": "0",
            "Comments": "Comments"
        },
        {
            "@search.score": 0.2553736,
            "Id": "35",
            "Date": "2017-08-24T09:14:56.193Z",
            "Domain": "domain.com",
            "RuleName": "Legacy Reports",
            "Log": "Log text",
            "ChangeId": "changeId",
            "ParentId": "0",
            "Comments": "Comments"
        }
    ]
}

My class looks like this:

[DataContract]
public class SearchResult
{
    [DataMember]
    public float SearchScore { get; set; }
    [DataMember]
    public string Id { get; set; }
    [DataMember]
    public DateTime Date { get; set; }
    [DataMember]
    public string Domain { get; set; }
    [DataMember]
    public string RuleName { get; set; }
    [DataMember]
    public string Log { get; set; }
    [DataMember]
    public string ChangeId { get; set; }
    [DataMember]
    public string ParentId { get; set; }
    [DataMember]
    public string Comments { get; set; }
}

My code in the controller to handle the response here:

try
{
    WebResponse response = request.GetResponse();
    using (Stream responseStream = response.GetResponseStream())
    {
        StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);

        DataContractJsonSerializer serializer = 
            new DataContractJsonSerializer(typeof(IEnumerable<SearchResult>));
        var results = 
            (IEnumerable<SearchResult>)serializer.ReadObject(responseStream);

        ViewBag.SearchResults = results;
    }
}
catch (WebException ex)
{
    WebResponse errorResponse = ex.Response;
    using (Stream responseStream = errorResponse.GetResponseStream())
    {
        StreamReader reader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8"));
        ViewBag.SearchError = reader.ReadToEnd();
        ViewBag.SearchResults = "0 Results";

    }
    throw;
}

And finally, I'm trying to loop through the results in my view:

@foreach (var searchResult in ViewBag.SearchResults)
{
    @searchResult.Id <br />
}

I'm just getting an empty page. No exceptions when I test in VS.

2 Answers 2

6

Using Json.Net instead of DataContractJsonSerializer is easier.

First Your model :

public class Value
{
    [JsonProperty("@search.score")]
    public double SearchScore { get; set; }
    public string Id { get; set; }
    public string Date { get; set; }
    public string Domain { get; set; }
    public string RuleName { get; set; }
    public string Log { get; set; }
    public string ChangeId { get; set; }
    public string ParentId { get; set; }
    public string Comments { get; set; }
}

public class RootObject
{
    [JsonProperty("@odata.context")]
    public string Contextcontext { get; set; }
    public List<Value> value { get; set; }
}

The serialization code :

var result = JsonConvert.DeserializeObject<RootObject>(reader.ReadToEnd());

EDIT

If you really want to use DataContractJsonSerializer way, your model would be

[DataContract]
public class SearchResult
{
    [DataMember(Name = "@search.score")]
    public float SearchScore { get; set; }
    [DataMember]
    public string Id { get; set; }
    [DataMember]
    public DateTime Date { get; set; }
    [DataMember]
    public string Domain { get; set; }
    [DataMember]
    public string RuleName { get; set; }
    [DataMember]
    public string Log { get; set; }
    [DataMember]
    public string ChangeId { get; set; }
    [DataMember]
    public string ParentId { get; set; }
    [DataMember]
    public string Comments { get; set; }
}

[DataContract]
public class RootObject
{
    [DataMember(Name = "@odata.context")]
    public string Context { get; set; }
    [DataMember]
    public List<SearchResult> value { get; set; }
}

Your serialization code (see the DateTime handling code)

var settings = new DataContractJsonSerializerSettings()
{
    DateTimeFormat = new DateTimeFormat(@"yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture)
};
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(RootObject),settings);

var results =  (RootObject)serializer.ReadObject(responseStream);
Sign up to request clarification or add additional context in comments.

Comments

3

The deserialization issue you are experiencing is due to the Azure search response wrapping the search results inside the value array. One way to read the results would be to create your own wrapper object that mirrors the Azure payload.

For example:

public class AzureSearchResponse
{
    [JsonProperty("@odata.context")]
    public string ODataContext { get; set; }
    public List<SearchResult> value { get; set; }
}

You may find it helpful to use the Newtonsoft.Json library, which can be installed using the Nuget command:

Install-Package Newtonsoft.Json

You can then deserialize with a command such as:

var searchResult = JsonConvert.DeserializeObject<AzureSearchResponse>(reader.ReadToEnd());

For property names that do not match your CLR property name decorate with attribute

[JsonProperty(PropertyName = "@search.score")]

No need for [DataContract] and [DataMember] attributes

With regard getting the data into your view, I recommend that you use a View Model as opposed to the ViewBag. You could create a ViewModel that is essentially your deserialized search results, and iterate over that in your razor.

At the top of your view decorate with model keyword, such as:

@model Namespace.Path.To.Your.Model

Your model class could contain the List and then use that in your loop, e.g.

<ul>
@foreach(var result in Models.Results) {
    <li>@result.RuleName</li>
}
</ul>

Hope this helps, let me know how you get on.

Regards Phil

2 Comments

Great answer and detailed perfectly. Nice job
This is one of the best answers I've received on a help forum. Thank you for the detail, and for the tip on ViewModels. I'm now parsing Azure search results. Thanks again!

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.