Parsing a Json object that contains a field with two distinct types, one of which is recursive

In my project, I have a TypeScript component that retrieves JSON data, and I need to parse this JSON in C# to work with the objects. However, due to the presence of:

  1. multiple type fields
  2. recursion

it's becoming challenging to understand how the deserialization process should be implemented. I am aware that I need to use a JsonConverter for handling multiple type fields, but I'm unsure how to handle recursion in conjunction with multiple type fields.

Below is the TypeScript code responsible for generating the JSON:

export interface FilterDescriptor {

  field ? : string | Function;

  operator: string | Function;

  value ? : any;

  ignoreCase ? : boolean;
}

export interface CompositeFilterDescriptor {
  logic: 'or' | 'and';

  filters: Array < FilterDescriptor | CompositeFilterDescriptor > ;
}

export declare
const isCompositeFilterDescriptor: (source: FilterDescriptor | CompositeFilterDescriptor) => source is CompositeFilterDescriptor;

Here are some examples of the generated JSON:

With recursion:

{
"logic": "and",
"filters": [
  {
    "field": "type.group",
    "logic": "and",
    "filters": [
      {
        "field": "type.group",
        "operator": "neq",
        "value": 2
      },
      {
        "field": "type.group",
        "operator": "neq",
        "value": 5
      }
    ]
  }
]}

Without recursion:

{
"logic": "and",
"filters": [
  {
    "field": "type.group",
    "operator": "eq",
    "value": 2
  }
]}

This JSON is generated using Kendo UI for Angular from Telerik's "CompositeFilterDescriptor."

Thank you.

Answer №1

The challenge here lies in the fact that C# lacks an equivalent feature to the 'discriminated union' concept (or that's what I believe it's referred to as ;-))

When it comes to achieving polymorphism in C#, your options are limited to interfaces and inheritance.

For this particular scenario, you require two types (FilterDescriptor and CompositeFilterDescriptor) and the use of polymorphism is necessary because CompositeFilterDescriptor contains an array that can house either of these types. In C#, you would typically utilize an interface or base class to represent this:

interface IFilterDescriptor { }

class FilterDescriptor : IFilterDescriptor
{
    // ... (fields of FilterDescriptor)
}

class CompositeFilterDescriptor : IFilterDescriptor
{
    public string @logic { get; set; }
    public IFilterDescriptor[] filters { get; set; }
}

Moreover, default deserialization with Json.NET cannot be used in this case, as it requires type information for polymorphic deserialization. Alternatively, you can create a custom converter where you can analyze specific fields to determine how to deserialize an object:

public class FilterDescriptorConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => typeof(IFilterDescriptor).IsAssignableFrom(objectType);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject o = JObject.Load(reader);

        // if the 'logic' field is present, it's a CompositeFilter
        var item = o["logic"] != null 
            ? (IFilterDescriptor)new CompositeFilterDescriptor() 
            : (IFilterDescriptor)new FilterDescriptor();
        serializer.Populate(o.CreateReader(), item);
        return item;
    }

    public override bool CanWrite => false;
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

Usage example:

var result = JsonConvert.DeserializeObject<IFilterDescriptor>(json, 
          new FilterDescriptorConverter());

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The procedure or process has not been executed

There are two different forms here. Form2 is a modified version of Form1. However, I am encountering an issue with the design mode of Form2, as can be seen in the screenshot below. If I comment out this line this._presenter.Retrive();, everything works p ...

Instructions for programmatically deleting a file from an SFTP server using SharpSSH

Looking for guidance on using Tamir Gal's SharpSSH to delete a file from a SFTP server. I have successfully implemented other features, but struggling with deletion. Any advice would be greatly appreciated. ...

Using JSON with Arduino for making a POST request

Currently, I'm working on a project where I am trying to configure my Arduino (equipped with an Ethernet shield) to send a POST request containing a JSON body to my local server. For this task, I have incorporated the ArduinoJson library (version 6) ...

drop-down menu displaying the major cities will be at the top

Hey, I'm trying to populate the combo box with cities from a database, but I want the metropolitan cities to be at the top of the selection menu. I have implemented this approach, is there an alternative method? For metropolitan cities: List<Lst_ ...

What is the best way to call an Angular component function from a global function, ensuring compatibility with IE11?

Currently, I am facing a challenge while integrating the Mastercard payment gateway api into an Angular-based application. The api requires a callback for success and error handling, which is passed through the data-error and data-success attributes of the ...

Binding data in a Listbox on Windows Phone 8.0

I am currently facing an issue with loading data into a ListView within a ListBox using web services PHP API. Despite my efforts, the data is not being loaded successfully. Below is my JSON class structure: public class Menu { public string Me ...

Exploring C# Programming with Jack

I have an application in development that will be responsible for recording audio from a microphone. One challenge I am facing is determining when the user plugs or unplugs their headphones. After exploring the Win32_SoundDevice WMI class, it appears ther ...

Angular Delight: Jaw-Dropping Animation

After setting up my first Angular project, I wanted to incorporate Angular Animations to bring life to my content as the user scrolls through the page. I aimed to not only have the content appear on scroll but also implement a staggering animation effect. ...

Incorrect typings being output by rxjs map

combineLatest([of(1), of('test')]).pipe( map(([myNumber, myString]) => { return [myNumber, myString]; }), map(([myNewNumber, myNewString]) => { const test = myNewString.length; }) ); Property 'length' does not ...

openapi-generator is generating subpar api documentation for TypeScript

Executing the command below to generate an auto-generated API using openapi-generator (v6.0.1 - stable): openapi-generator-cli generate -i app.json -g typescript -o src/main/api The json file is valid. Validation was done using openapi-generator-cli valid ...

Plugin refresh after callback

I have a webpage that features a row of buttons at the top, followed by some content below. Whenever one of the buttons is clicked, the content is updated using UpdatePanels. Within this content, I am attempting to incorporate the royal slider plugin, wh ...

Is there a more efficient method for providing hooks to children in React when using TypeScript?

My component structure looks something like this: Modal ModalTitle ModalBody FormElements MySelect MyTextField MyCheckbox DisplayInfo ModalActions I have a state variable called formVars, and a function named handleAction, ...

What is the best way to transform a JavaScript object into an array?

Here is the information I have: {product_quantity: {quantity: 13, code: "AAA", warehouse: "1000",}} The product_quantity field is part of a JSON object in a MongoDB database. I am looking to convert it into this format: {"produ ...

Create new instances of Backbone Models using an existing Backbone Model as a template

Within my app, I am planning to include a configuration file called config.json as a Backbone Model. Here is an example of how it will be loaded: var Config = Backbone.Model.extend({ defaults: { base: '' }, url: 'config. ...

What is the process for checking pending requests within Azure Application Insights?

I successfully integrated the Azure Application Insights package into my .Net Core/Angular web application. Program.cs services.AddApplicationInsightsTelemetry(); In addition, I developed a front-end service for logging client-side events. @Injectable() ...

What is the maximum character limit for jQuery?

Here's a code snippet I'm working with: $.get("url") .done(function(data){ alert("success"); alert(JSON.stringify(data)); }) .fail(function(data){ alert("fail"); alert(JSON. ...

In Python/Django, the accent character is replaced with xed

The response from the server came in JSON format, but when I display it in the HTML template, the accents are replaced by \xed. (Using the Django framework) Using json.dumps gives a perfect result, but then I can't access it in the template like ...

A guide on retrieving JSON data from an AJAX request using Angular UI Bootstrap

Greetings Friends, I have experience with traditional JavaScript and a little bit of JQuery. Now, for my new JAVA Web Based project, we need to utilize the latest technology to quickly build dynamic web pages. I don't have time to test all the widget/ ...

The JSON Post Request functionality is encountering issues within ASP.NET5 MVC, whereas it is functioning properly in ASP.NET4.5 MVC

I am encountering an issue with a JSON post request to an MVC controller. It works fine in ASP.NET 4.5 but fails in the latest ASP.NET 5 release. Am I overlooking something in my setup? I have created a model, but I haven't bound it to a database. ...

Refine the JSON data to only include the values you need

I've been spending a considerable amount of time on this challenge, but I'm facing difficulty in solving it. The issue at hand is that I have a JSON file that I am fetching from a URL, and my objective is to create a filter that displays only nam ...