1

I currently have an API which I use dapper to connect to a SQL database to find the file path for documents related to a certain ID. The result is returned in JSON format as follows:

[{"DOC-ID": 1, "DOCUMENT_FULL_PATH": "/PATH/FILENAME.DOC"},{"DOC-ID": 2, "DOCUMENT_FULL_PATH": "/PATH/FILENAME2.DOC"}]

I am trying to get my API to deserialize the JSON data then link that too a model (I prefer to not use models but the only solution I found was using JSON DOM which is briefly discussed on MS website but does not provide an example of how to loop through the JSON array so I could not move forward with this example). When I try to deserialize the dapper query result I get the error indicated below (shown at the line item in the code). I am not sure what is triggering this as I would think the QuerySingle could be deserialized with this method. Once this error is fixed I need to check the files last modified date and save that value to the model which I then again need to serialize to send to the front end! I have spent so much time on this so some help would be much appreciated!

[HttpPost]
public ActionResult MainReviewDocuments([FromForm] string ID)
{
    //Using FormData on frontend
    //checking ID exists on searching in dashboard
    if (ID == null || ID.Length == 0)
    {
        return Ok(new { Result = "Failed" });
    }
    else
    {
        //We have parameters here just in case we want to use them
        var UserID = HttpContext.User.FindFirst(ClaimTypes.Name).Value;

        String query = "select dbo.A2Q_0132W_RR_IDDocumentation_JSON(@ID) as output";

        using (var connection = new SqlConnection(connectionString))
        {
            var json = connection.QuerySingle<string>(query, new { ID = ID}); 

            MainReviewDocuments? mainreviewdocuments = JsonSerializer.Deserialize<MainReviewDocuments>(json); // this line draws an error 'The JSON value could not be converted to Project.Models.MainReviewDocuments. Path: $ | LineNumber: 0 | BytePositionInLine: 1.'
            var rootPath = Path.Combine(Directory.GetParent(env.ContentRootPath).ToString(), "Files");

            foreach (var document in mainreviewdocuments)
            {
                filePath = Path.Combine(rootPath, document.DOCUMENT_FULL_PATH);
                //Check file system for each file path and set last modified value to model object LAST_MODIFIED. Struggling with this as well
            }
            return Ok(mainreviewdocuments); // Can I use Ok() method to convert model back to JSON?
        }
    }
}
Amit Joshi
  • 15,448
  • 21
  • 77
  • 141
Irish Redneck
  • 983
  • 7
  • 32

1 Answers1

1

In your original call, you need to de-serialize to a List:

MainReviewDocuments? mainreviewdocuments = JsonSerializer.Deserialize<List<MainReviewDocuments>>(json);

and then access your properties are required.

Using Newtonsoft.Json library:

You can de-serialize your JSON string that you receive from your DB into the following class:

public class MainReviewDocuments
{
    [JsonProperty("DOC-ID")]
    public int DOCID { get; set; }
    public string DOCUMENT_FULL_PATH { get; set; }
}

Or you can use dynamic to de-serialize your JSON:

var mainreviewdocuments = JsonSerializer.Deserialize<dynamic>(json);

and then access the properties as shown in the example below.

You can refer to a working example below:

using System;
using Newtonsoft.Json;
using System.Collections.Generic;
                    
public class Program
{
    public static void Main()
    {
        var myJsonString=@"[{'DOC-ID': 1, 'DOCUMENT_FULL_PATH': '/PATH/FILENAME.DOC'},{'DOC-ID': 2, 'DOCUMENT_FULL_PATH': '/PATH/FILENAME2.DOC'}]";
        var mainreviewdocuments =JsonConvert.DeserializeObject<List<MainReviewDocuments>>(myJsonString);
        Console.WriteLine("Example using Model: \n");
        foreach(var item in mainreviewdocuments)
        {
            Console.WriteLine(item.DOCID);
            Console.WriteLine(item.DOCUMENT_FULL_PATH);         
        }
        Console.WriteLine("\n");
        Console.WriteLine("Example using Dynamic: \n");
        
        //Example using dynamic
        var mainreviewdocumentsDynamic=JsonConvert.DeserializeObject<dynamic>(myJsonString);
        foreach(var item in mainreviewdocumentsDynamic)
        {
            Console.WriteLine(item["DOC-ID"]);
            Console.WriteLine(item["DOCUMENT_FULL_PATH"]);      
        }
    }
}

public class MainReviewDocuments
{
    [JsonProperty("DOC-ID")]
    public int DOCID { get; set; }
    public string DOCUMENT_FULL_PATH { get; set; }
}

Output:

Example using Model: 

1
/PATH/FILENAME.DOC
2
/PATH/FILENAME2.DOC


Example using Dynamic: 

1
/PATH/FILENAME.DOC
2
/PATH/FILENAME2.DOC

Using System.Text.Json library:

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
                
public class Program
{
    public static void Main()
    {       
       var myJsonString="[{\"DOC-ID\": 1, \"DOCUMENT_FULL_PATH\": \"/PATH/FILENAME.DOC\"},{\"DOC-ID\": 2, \"DOCUMENT_FULL_PATH\": \"/PATH/FILENAME2.DOC\"}]";
       var mainreviewdocuments = JsonSerializer.Deserialize<List<MainReviewDocuments>>(myJsonString);
        Console.WriteLine("Example using Model: \n");
        foreach(var item in mainreviewdocuments)
        {
            Console.WriteLine(item.DOCID);
            Console.WriteLine(item.DOCUMENT_FULL_PATH);         
        }
        
        Console.WriteLine("\n");
        Console.WriteLine("Example using Dynamic: \n");
        using (JsonDocument document = JsonDocument.Parse(myJsonString))
        {
           foreach (JsonElement element in document.RootElement.EnumerateArray())
           {
               Console.WriteLine(element.GetProperty("DOC-ID"));
               Console.WriteLine(element.GetProperty("DOCUMENT_FULL_PATH"));
            }
        }
    }
}


public class MainReviewDocuments
{
    [JsonPropertyName("DOC-ID")]
    public int DOCID { get; set; }
    public string DOCUMENT_FULL_PATH { get; set; }
}

Output:

Example using Model: 

1
/PATH/FILENAME.DOC
2
/PATH/FILENAME2.DOC


Example using Dynamic: 

1
/PATH/FILENAME.DOC
2
/PATH/FILENAME2.DOC

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

You can read more on the comparison between the two libraries in this article

Rahul Sharma
  • 7,768
  • 2
  • 28
  • 54
  • Thank you for this! I am actually still drawing an error for both methods. I know my query returns a JSON string but using the class method and using List it will not compile due to cannot implicitly convert type 'System.Collections.Generic.List' to 'Project.Models.MainReviewDocuments' . Using the method it indicates Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'Cannot implicitly convert type 'System.Text.Json.JsonElement' to 'System.Collections.IEnumerable''. My JSON array when returned does not have a @. That ok? – Irish Redneck Mar 17 '22 at 01:55
  • I also do not use JsonConvert which I hope is okay. I use JsonSerializer which is a default MS library. Could that be the issue? – Irish Redneck Mar 17 '22 at 01:56
  • @Bradyboyy88 Can you please post your `MainReviewDocuments` model class please? Have you tried using `Newtonsoft` package for parsing your `JSON`? You can parse with dynamic using the default MS library: `var mainreviewdocuments = JsonNode.Parse(json);` – Rahul Sharma Mar 17 '22 at 05:41
  • I have used newtonsoft and your code does work. I was just trying to stick with MS libraries rather than third party but I cannot for the life of me make it work ha. My model is very similar to what you put but the code I did above is just a dumbed down version due to confidentiality. Does your example work using MS serializer()?. – Irish Redneck Mar 17 '22 at 19:45
  • @Bradyboyy88 I have updated my answer to include `System.Text` library also for your scenario. Hope it helps you out. – Rahul Sharma Mar 17 '22 at 20:11
  • Just curious can system.text.json also use dynamic similar to JsonConvert? And any reason to use JsonConvert over system.text.json? – Irish Redneck Mar 19 '22 at 00:31
  • @Bradyboyy88 I have updated my answer to include the above `dynamic` scenario and also a link to an article outlining the differences between the two libraries. If my answer helped you out, you can accept it and up-vote. Thanks – Rahul Sharma Mar 19 '22 at 11:50
  • There is also one last thing. There is a property I am adding to the deserialized JSON array called LAST_MODIFIED which is basically the files on the servers last modified date. I know how to get the value but I do not know how to add it to the JsonElement for dynamic. I see you can use element.GetProperty() to get the value but how can I add property and set a property value as I loop through each element? Not seeing a method for that. For JSONConvert it was easy but I am trying to use the system.text.json example you created as the article you reference kind of points towards using it. – Irish Redneck Mar 21 '22 at 15:50
  • @Bradyboyy88 Okay, so you have a property that is not a part of your `JSON` and you want to access that property while accessing your elements of your `JSON` string? How are you doing that? Can you give an example? – Rahul Sharma Mar 22 '22 at 06:37
  • Using your code in the foreach loop I need to add a property "LAST_MODIFIED" and set its value equal to something, say a string called "testvalue". Is there a method to do this? – Irish Redneck Mar 28 '22 at 17:18
  • @Bradyboyy88 You can refer to this question for your purpose if I am understanding your query correctly: https://stackoverflow.com/questions/58302522/how-to-add-property-in-existing-json-using-system-text-json-library – Rahul Sharma Mar 28 '22 at 17:28