Skip to the content.

How to parse JSON from curl in C#

To parse JSON from a curl response in C#, run the command with Curl.ExecuteAsync and call result.ParseJson<T>() to deserialize result.Body into your type. For quick, untyped access use result.AsJsonDynamic() or ParseJson<JsonElement>().

dotnet add package CurlDotNet

Typed: ParseJson()

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

var result = await Curl.ExecuteAsync("curl https://httpbin.org/json");
var doc = result.ParseJson<SlideDoc>();
Console.WriteLine(doc.Slideshow.Title);  // Sample Slide Show

public record SlideDoc([property: JsonPropertyName("slideshow")] Slideshow Slideshow);
public record Slideshow(
    [property: JsonPropertyName("title")] string Title,
    [property: JsonPropertyName("author")] string Author);

AsJson<T>() is an alias for ParseJson<T>().

Watch the property-name casing

Which serializer ParseJson<T>() uses depends on the build of CurlDotNet your app loads, but property names match case-insensitively on every build:

Your app targets Serializer Property names
.NET 8 / 9 / 10, .NET Framework 4.7.2 / 4.8 System.Text.Json with PropertyNameCaseInsensitive = true case-insensitive
Anything that resolves the netstandard2.0 build (for example .NET 6 or 7) Newtonsoft.Json case-insensitive

So a C# property Title binds to JSON "title" without any attribute. Names that differ by more than case, such as snake_case keys ("created_at" to CreatedAt), still need [JsonPropertyName] (or [JsonProperty] for Newtonsoft). To control the options yourself, the extension overload in the CurlDotNet namespace accepts JsonSerializerOptions:

using System.Text.Json;
using CurlDotNet;

var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var result = await Curl.ExecuteAsync("curl https://httpbin.org/json");

var doc = result.ParseJson<SlideDoc>(options);
Console.WriteLine(doc.Slideshow.Title);

public record SlideDoc(Slideshow Slideshow);
public record Slideshow(string Title, string Author);

That overload also refuses to parse a failed response: it throws InvalidOperationException when IsSuccess is false.

Untyped: JsonElement or AsJsonDynamic()

using System.Text.Json;
using CurlDotNet;

var result = await Curl.ExecuteAsync("curl https://httpbin.org/json");
JsonElement root = result.ParseJson<JsonElement>();

Console.WriteLine(root.GetProperty("slideshow").GetProperty("title").GetString());

AsJsonDynamic() returns a System.Text.Json.JsonDocument on .NET 8+ and .NET Framework builds, and a Newtonsoft JObject on the netstandard2.0 build. Member access such as json.slideshow.title only works with the JObject. On .NET 8+, treat it as a JsonDocument:

using System.Text.Json;
using CurlDotNet;

var result = await Curl.ExecuteAsync("curl https://httpbin.org/json");
using JsonDocument json = result.AsJsonDynamic();
Console.WriteLine(json.RootElement.GetProperty("slideshow").GetProperty("title"));

The fluent builder

using CurlDotNet.Core;

var result = await CurlRequestBuilder
    .Get("https://httpbin.org/uuid")
    .WithHeader("Accept", "application/json")
    .ExecuteAsync();

var id = result.ParseJson<System.Text.Json.JsonElement>().GetProperty("uuid").GetString();

When parsing fails

Save the JSON

result.SaveAsJson("response.json") writes the body pretty-printed (pass indented: false for compact output).