Hwo to deserialize jqgrid filter in .NET 5

Andrus 121 Reputation points
2021-06-26T16:56:49.293+00:00

jqgrid filter deserialization in ASP.NET 5 MVC application fails using System.Text.Json.JsonSerializer

To reproduce, run the code

var _filters ="{\"groupOp\":\"AND\",\"rules\":[{\"field\":\"Toode\",\"op\":\"cn\",\"data\":\"\"}]}";

var filtersList = JsonSerializer.Deserialize<Filter>(_filters, new JsonSerializerOptions
                { 
                    PropertyNameCaseInsensitive = true
                });

Result:

The JSON value could not be converted to MyApp.Controllers.GroupOp. Path: $.groupOp | LineNumber: 0 | BytePositionInLine: 16.


System.Text.Json.JsonException: The JSON value could not be converted to MyApp.Controllers.GroupOp. Path: $.groupOp | LineNumber: 0 | BytePositionInLine: 16.
   at System.Text.Json.ThrowHelper.ThrowJsonException(String message)
   at System.Text.Json.Serialization.Converters.EnumConverter`1.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
   at System.Text.Json.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.JsonSerializer.ReadCore[TValue](Utf8JsonReader& reader, Type returnType, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, Type returnType, JsonSerializerOptions options)
   at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options)

jqgrid classes used in deserialization:

public class Filter
{
    public GroupOp GroupOp { get; set; }
    public List<Rule> Rules { get; set; }
    public List<Filter> Groups { get; set; }
}

public enum GroupOp
{
    AND,
    OR
}

public class Rule
{
    public string Field { get; set; }
    public Operations Op { get; set; }
    public string Data { get; set; }
}

public enum Operations
{
    eq,
    ne,
    cn,
    le
}

In .NET 4.6 it worked using

var serializer = new JavaScriptSerializer();
var filtersList = serializer.Deserialize<Filter>(_filters);

How to make it work in .NET 5 ?

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,663 questions
0 comments No comments
{count} votes

Accepted answer
  1. Zhi Lv - MSFT 32,546 Reputation points Microsoft Vendor
    2021-06-28T07:27:53.62+00:00

    Hi @Andrus ,

    For numeric properties jqgrid passes data property values as numbers like:

    "{\"groupOp\":\"AND\",\"rules\":[{\"field\":\"Baas\",\"op\":\"eq\",\"data\":\"TDESKTOP\"},{\"field\":\"Liigid\",\"op\":\"eq\",\"data\":\"\"},{\"field\":\"Layoutnumb\",\"op\":\"eq\",\"data\":0}]}"

    Trying to deserialize this causes exception

    Cannot get the value of a token type 'Number' as a string.
    The JSON value could not be converted to System.String. Path: $.rules[2].data | LineNumber: 0 | BytePositionInLine: 150.

    The new issue relates this item: {\"field\":\"Layoutnumb\",\"op\":\"eq\",\"data\":0}.

    For the data property, it is string type, but in the JSON string, this item is Number type, so it will cause this issue.

    To solve this issue, you could change the 0 to \"0\", the updated JSON string as below:

    "{\"groupOp\":\"AND\",\"rules\":[{\"field\":\"Baas\",\"op\":\"eq\",\"data\":\"TDESKTOP\"},{\"field\":\"Liigid\",\"op\":\"eq\",\"data\":\"\"},{\"field\":\"Layoutnumb\",\"op\":\"eq\",\"data\":\"0\"}]}".

    Besides, you could also use Regex to replace the number value to string:

    var _filters = "the new json string";  
    
    string pattern = @"(?<![""\w])(\d)(?![""\w])";    
    var result = Regex.Replace(_filters, pattern, "\"$1\"");              
    

    The result like this:

    109778-3.gif


    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best Regards,
    Dillion

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Ken Tucker 5,856 Reputation points
    2021-06-26T18:15:14.82+00:00

    Looks like you have a circular reference in the Filter class. Try something like this

            JsonSerializerOptions options = new()  
            {  
                ReferenceHandler = ReferenceHandler.Preserve,  
                WriteIndented = true  
            };  
    
            var filtersList = serializer.Deserialize<Filter>(_filters, options);  
    

    https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-preserve-references?pivots=dotnet-5-0


  2. Zhi Lv - MSFT 32,546 Reputation points Microsoft Vendor
    2021-06-28T02:58:57.683+00:00

    Hi @Andrus ,

    Since the JSON string and Model contains Enum data type, when deserialize the json string, you should add the JsonStringEnumConverter.

    Try to use the following code:

    var _filters = "{\"groupOp\":\"AND\",\"rules\":[{\"field\":\"Toode\",\"op\":\"cn\",\"data\":\"\"}]}";  
    
    //using System.Text.Json;  
    //using System.Text.Json.Serialization;  
    var options = new JsonSerializerOptions();  
    options.PropertyNameCaseInsensitive = true;  
    options.Converters.Add(new JsonStringEnumConverter());  
               
    var filtersList = JsonSerializer.Deserialize<Filter>(_filters, options);  
    

    Then, the result as below:

    109649-capture.png


    If the answer is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best Regards,
    Dillion


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.