Transform JSON with ChatGPT

Transform JSON with ChatGPT
by Brad Jolicoeur
11/17/2024

Mapping a JSON document to a model can be a time consuming and tedious task. Earlier today I had a situation where I needed to map a webhook JSON document to a model so that I could trigger a workflow from the webhook. After contemplating how much time this will take, I remembered ChatGPT is relatively good at this type of task and decided to try it out. In this article, I'll share my experience and hopefully you will find this trick helps you too.

JSON Webhook Event

In this case, I'm getting a webhook event each time a customer signs up. This contains important customer data I need to trigger a workflow to onboard the customer to my software.

{
    "data": {
        "id": "ctm_01jctzmkhbf1yt53fxxxxcn2qn",
        "name": null,
        "email": "brad@sample.com",
        "locale": "en",
        "status": "active",
        "created_at": "2024-11-16T17:16:56.747Z",
        "updated_at": "2024-11-16T17:16:56.747Z",
        "custom_data": null,
        "import_meta": null,
        "marketing_consent": false
    },
    "event_id": "evt_01jctzmktkpr9vhdd1nn5n809a",
    "event_type": "customer.created",
    "occurred_at": "2024-11-16T17:16:57.043331Z",
    "notification_id": "ntf_01jctzmkyytzzpypzvsjbbfzq9"
}

I need to map the JSON into this class.

Note that this was not the mapping that had me dreading the mapping task, there is a much more complicated one for subscriptions that I started with, but for the purposes of a blog article this simpler and smaller document will be easier reading.

public class InitializeCustomer
{
    public string CustomerId { get; set; } 
    public string CustomerName { get; set; }
    public string Email { get; set; }
    public string Status { get; set; }
    public string MarketingConsent { get; set; }
    public string NotificationId { get; set; }
}

The Prompt

With my example JSON string and a target class to map to, I was ready for the ChatGPT prompt.

I started with some general instructions on what I was looking for and provided the JSON example and class I'd like to target.

I'm requesting 'csx' format so that I get a C# script instead of a console application. I do this so that I can quickly test the results using a Polyglot Notebook in VSCode.

Please create c# code in csx format to transform the provided json into the provided c# class.  Use system.text.json instead of Newtonsoft and dynamic class to deserialize the JSON string.

JSON: {"data":{"id":"ctm_01jctzmkhbf1yt53fxxxxcn2qn","name":null,"email":"brad@sample.com","locale":"en","status":"active","created_at":"2024-11-16T17:16:56.747Z","updated_at":"2024-11-16T17:16:56.747Z","custom_data":null,"import_meta":null,"marketing_consent":false},"event_id":"evt_01jctzmktkpr9vhdd1nn5n809a","event_type":"customer.created","occurred_at":"2024-11-16T17:16:57.043331Z","notification_id":"ntf_01jctzmkyytzzpypzvsjbbfzq9"}

class:
public class InitializeCustomer
{
    public string CustomerId { get; set; } 
    public string CustomerName { get; set; }
    public string Email { get; set; }
    public string Status { get; set; }
    public string MarketingConsent { get; set; }
    public string NotificationId { get; set; }
}

The resulting code generated was as follows:

#r "System.Text.Json"

using System;
using System.Text.Json;

public class InitializeCustomer
{
    public string CustomerId { get; set; } 
    public string CustomerName { get; set; }
    public string Email { get; set; }
    public string Status { get; set; }
    public string MarketingConsent { get; set; }
    public string NotificationId { get; set; }
}

// JSON input
var jsonString = @"{
    ""data"": {
        ""id"": ""ctm_01jctzmkhbf1yt53fxxxxcn2qn"",
        ""name"": null,
        ""email"": ""brad@sample.com"",
        ""locale"": ""en"",
        ""status"": ""active"",
        ""created_at"": ""2024-11-16T17:16:56.747Z"",
        ""updated_at"": ""2024-11-16T17:16:56.747Z"",
        ""custom_data"": null,
        ""import_meta"": null,
        ""marketing_consent"": false
    },
    ""event_id"": ""evt_01jctzmktkpr9vhdd1nn5n809a"",
    ""event_type"": ""customer.created"",
    ""occurred_at"": ""2024-11-16T17:16:57.043331Z"",
    ""notification_id"": ""ntf_01jctzmkyytzzpypzvsjbbfzq9""
}";

// Parse JSON dynamically
using var doc = JsonDocument.Parse(jsonString);
var root = doc.RootElement;

// Map to InitializeCustomer class
var customer = new InitializeCustomer
{
    CustomerId = root.GetProperty("data").GetProperty("id").GetString(),
    CustomerName = root.GetProperty("data").TryGetProperty("name", out var name) && name.ValueKind != JsonValueKind.Null ? name.GetString() : null,
    Email = root.GetProperty("data").GetProperty("email").GetString(),
    Status = root.GetProperty("data").GetProperty("status").GetString(),
    MarketingConsent = root.GetProperty("data").GetProperty("marketing_consent").GetBoolean().ToString(),
    NotificationId = root.GetProperty("notification_id").GetString()
};

// Output for verification
Console.WriteLine($"CustomerId: {customer.CustomerId}");
Console.WriteLine($"CustomerName: {customer.CustomerName}");
Console.WriteLine($"Email: {customer.Email}");
Console.WriteLine($"Status: {customer.Status}");
Console.WriteLine($"MarketingConsent: {customer.MarketingConsent}");
Console.WriteLine($"NotificationId: {customer.NotificationId}");

This code looks like exactly what I want. But there is one thing I've learned with ChatGPT is that it will generate code that looks right, but has some issues. In this case there are a couple of small things wrong with this code.

To quickly test this code identify any issues and validate that it works, I used a Polyglot Notebook in VSCode. This extension is super helpful for prototyping and testing small blocks of code. Since we requested the code be generated in CSX format we can drop this code right in a notebook and verify that it works.

Fixing things up

Right away, I see that there is a compile issue when I drop the script into the notebook.

using var doc = JsonDocument.Parse(jsonString);

This line has a Red underline on 'doc' and the code will not compile. The JsonDocument results of Parse implements IDisposable so ChatGPT utilized the using statement, but it is not encapsulated in a method so this syntax will not work. We could fix this code up in the notebook, but lets get ChatGPT to fix it's own work.

In the chat I asked, can you encapsulate the using statement in a method?. This time I had better luck and cleaner code for copying into my production code base.

#r "System.Text.Json"

using System;
using System.Text.Json;

public class InitializeCustomer
{
    public string CustomerId { get; set; } 
    public string CustomerName { get; set; }
    public string Email { get; set; }
    public string Status { get; set; }
    public string MarketingConsent { get; set; }
    public string NotificationId { get; set; }
}

public InitializeCustomer ParseCustomerJson(string jsonString)
{
    using var doc = JsonDocument.Parse(jsonString);
    var root = doc.RootElement;

    return new InitializeCustomer
    {
        CustomerId = root.GetProperty("data").GetProperty("id").GetString(),
        CustomerName = root.GetProperty("data").TryGetProperty("name", out var name) && name.ValueKind != JsonValueKind.Null ? name.GetString() : null,
        Email = root.GetProperty("data").GetProperty("email").GetString(),
        Status = root.GetProperty("data").GetProperty("status").GetString(),
        MarketingConsent = root.GetProperty("data").GetProperty("marketing_consent").GetBoolean().ToString(),
        NotificationId = root.GetProperty("notification_id").GetString()
    };
}

// JSON input
var jsonString = @"{
    ""data"": {
        ""id"": ""ctm_01jctzmkhbf1yt53fxxxxcn2qn"",
        ""name"": null,
        ""email"": ""brad@sample.com"",
        ""locale"": ""en"",
        ""status"": ""active"",
        ""created_at"": ""2024-11-16T17:16:56.747Z"",
        ""updated_at"": ""2024-11-16T17:16:56.747Z"",
        ""custom_data"": null,
        ""import_meta"": null,
        ""marketing_consent"": false
    },
    ""event_id"": ""evt_01jctzmktkpr9vhdd1nn5n809a"",
    ""event_type"": ""customer.created"",
    ""occurred_at"": ""2024-11-16T17:16:57.043331Z"",
    ""notification_id"": ""ntf_01jctzmkyytzzpypzvsjbbfzq9""
}";

// Call the method
var customer = ParseCustomerJson(jsonString);

// Output for verification
Console.WriteLine($"CustomerId: {customer.CustomerId}");
Console.WriteLine($"CustomerName: {customer.CustomerName}");
Console.WriteLine($"Email: {customer.Email}");
Console.WriteLine($"Status: {customer.Status}");
Console.WriteLine($"MarketingConsent: {customer.MarketingConsent}");
Console.WriteLine($"NotificationId: {customer.NotificationId}");

This code worked correctly on the first try and had no compile issues. That said, System.Text.Json does not require a NuGet package so we can just drop #r "System.Text.Json" from the script since it doesn't do anything.

Now that we have output that we like and compiles, we might want to create similar transformations. Let's ask ChatCPT to reformulate our original prompt to generate the output we like by asking, Can you reformat my original prompt so that I get this output?.

Here is the prompt ChatGPT formulated for us to use. Conceptually, we should be able to replace the JSON and destination class with any new JSON to class transformations we need to generate and get reasonably good results.

**Request:**

Please create C# code in `.csx` format that transforms the provided JSON into the provided C# class, using `System.Text.Json` for deserialization. The code should include a method to encapsulate the JSON parsing, returning an instance of the C# class.

**JSON:**
{
    "data": {
        "id": "ctm_01jctzmkhbf1yt53fxxxxcn2qn",
        "name": null,
        "email": "brad@sample.com",
        "locale": "en",
        "status": "active",
        "created_at": "2024-11-16T17:16:56.747Z",
        "updated_at": "2024-11-16T17:16:56.747Z",
        "custom_data": null,
        "import_meta": null,
        "marketing_consent": false
    },
    "event_id": "evt_01jctzmktkpr9vhdd1nn5n809a",
    "event_type": "customer.created",
    "occurred_at": "2024-11-16T17:16:57.043331Z",
    "notification_id": "ntf_01jctzmkyytzzpypzvsjbbfzq9"
}


**Class:**
public class InitializeCustomer
{
    public string CustomerId { get; set; } 
    public string CustomerName { get; set; }
    public string Email { get; set; }
    public string Status { get; set; }
    public string MarketingConsent { get; set; }
    public string NotificationId { get; set; }
}

When I tested this prompt in a new ChatGPT thread, I actually got even cleaner code that worked with no modifications. In this response we get a class with a static method to encapsulate the parsing logic.

#r "nuget: System.Text.Json"

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

public class InitializeCustomer
{
    public string CustomerId { get; set; } 
    public string CustomerName { get; set; }
    public string Email { get; set; }
    public string Status { get; set; }
    public string MarketingConsent { get; set; }
    public string NotificationId { get; set; }
}

public class JsonParser
{
    public static InitializeCustomer ParseJson(string json)
    {
        var document = JsonDocument.Parse(json);
        var root = document.RootElement;

        var data = root.GetProperty("data");

        return new InitializeCustomer
        {
            CustomerId = data.GetProperty("id").GetString(),
            CustomerName = data.GetProperty("name").GetString(),
            Email = data.GetProperty("email").GetString(),
            Status = data.GetProperty("status").GetString(),
            MarketingConsent = data.GetProperty("marketing_consent").GetBoolean() ? "true" : "false",
            NotificationId = root.GetProperty("notification_id").GetString()
        };
    }
}

// Example usage
var json = @"{
    ""data"": {
        ""id"": ""ctm_01jctzmkhbf1yt53fxxxxcn2qn"",
        ""name"": null,
        ""email"": ""brad@sample.com"",
        ""locale"": ""en"",
        ""status"": ""active"",
        ""created_at"": ""2024-11-16T17:16:56.747Z"",
        ""updated_at"": ""2024-11-16T17:16:56.747Z"",
        ""custom_data"": null,
        ""import_meta"": null,
        ""marketing_consent"": false
    },
    ""event_id"": ""evt_01jctzmktkpr9vhdd1nn5n809a"",
    ""event_type"": ""customer.created"",
    ""occurred_at"": ""2024-11-16T17:16:57.043331Z"",
    ""notification_id"": ""ntf_01jctzmkyytzzpypzvsjbbfzq9""
}";

var customer = JsonParser.ParseJson(json);
Console.WriteLine($"CustomerId: {customer.CustomerId}");
Console.WriteLine($"CustomerName: {customer.CustomerName}");
Console.WriteLine($"Email: {customer.Email}");
Console.WriteLine($"Status: {customer.Status}");
Console.WriteLine($"MarketingConsent: {customer.MarketingConsent}");
Console.WriteLine($"NotificationId: {customer.NotificationId}");

Conclusions

I've had mixed results with generating code with ChatGPT in the past. What I learned in this experience is that following a few tricks can help you generate useful code snips to save time and tedium can be very effective.

Tips

  • Use Polyglot Notebooks to quickly check ChatGPT's work to save time and find bugs quickly in code that looks convincing but will not compile.
  • When you find bugs ask ChatGPT to fix the bugs instead of trying to fix them yourself
  • When you use ChatGPT to fix the bugs it now has more context and you can use it formulate a better prompt for future requests

You May Also Like


Convert HTML into JSON using Semantic Kernel and OpenAI

solvingsomethingawesome.jpg
Brad Jolicoeur - 09/28/2024
Read

Fabricate Sample Data with ChatGPT

fall-road.jpg
Brad Jolicoeur - 08/24/2024
Read

Data Analysis with C#: Leveraging .NET for High-Performance Tasks

in-isolation.JPG
Brad Jolicoeur - 08/17/2024
Read