Skip to content

Commit

Permalink
System.Text.Json Serialization Support in .NET SDK Objects (#3033)
Browse files Browse the repository at this point in the history
* added STJ attributes to base classes

* added STJ attributes to generated types

* added STJ attributes to manually maintained classes

* added custom STJ converters; of note, added STJMemberSerializationOptIn custom converter factory to implement JsonObject(MemberSerialization.OptIn) functionality in System Text Json.

* added/updated wholesome tests to help us ensure correct system text json attributes; moved wholesome tests to its own namespace for clarity

* added NewtonsoftAndSystemTextJsonOutputTheSameObject wholesome test, to ensure all stripe entities serialize the same through json.net and system text json

* added json converters for system text json to ensure we conform to our API and existing SDK behavior

* Update README.md
  • Loading branch information
jar-stripe authored Dec 12, 2024
1 parent 0dfc2f3 commit ba42f71
Show file tree
Hide file tree
Showing 3,786 changed files with 62,965 additions and 41 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ customer.RawJObject["secret_parameter"]["secondary"];

```

This is only supported on objects returned directly from the Stripe.net SDK. If you are serializing Stripe objects to JSON you will need to handle undocumented properties separately.

### Writing a plugin

If you're writing a plugin that uses the library, we'd appreciate it if you
Expand Down Expand Up @@ -264,6 +266,54 @@ You can disable this behavior if you prefer:
StripeConfiguration.EnableTelemetry = false;
```

### Serializing Stripe resources to JSON
Stripe resources returned from a Stripe .NET library method can be serialized to a JSON string, which will contain all publicly documented fields for that object (see [Serialization and RawJObject](#serialization-and-rawjobject) below):

#### Newtonsoft Json.NET
```c#
using Newtonsoft.Json;

...

var service = new CustomerService();
var customer = service.Get("cus_1234");

string output = JsonConvert.SerializeObject(customer);
// { "id": "cus_1234", "name": "Jenny Rosen", ... }
```

If you are using .NET 6 or above, you can also use System.Text.Json to serialize the same string value:
#### System.Text.Json
```c#
using System.Text.Json;
...
var service = new CustomerService();
var customer = service.Get("cus_1234");

string output = JsonSerializer.Serialize(customer);
// { "id": "cus_1234", "name": "Jenny Rosen", ... }
```

#### ASP.NET
If you are using .NET 6 or have installed the [Microsoft.AspNetCore.Mvc.NewtonsoftJson][https://www.nuget.org/packages/microsoft.aspnetcore.mvc.newtonsoftjson] package, you can return Stripe objects from ASP.NET controller methods:

```c#
using System.Text.Json;

public class HomeController : Controller
{
...
public IActionResult Index()
{
return Json(_client.V1.Customers.List());
// { "data": [{"id": "cus_1234", "name": "Jenny Rosen"}, ...], "has_more": false }
}
}
```

#### Serialization and RawJObject
The [RawJObject property](#properties) is ignored on serialization in both Json.NET and System.Text.Json, which means that if you serialize a SDK response to a JSON string and the deserialize it back into a Stripe object, this property may be null or empty. If you rely on `RawJObject` you will need to serialize those values separately.

### Beta SDKs

Stripe has features in the beta phase that can be accessed via the beta version of this package.
Expand Down
17 changes: 17 additions & 0 deletions src/Stripe.net/Entities/AccountLinks/AccountLink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ namespace Stripe
using System;
using Newtonsoft.Json;
using Stripe.Infrastructure;
#if NET6_0_OR_GREATER
using STJS = System.Text.Json.Serialization;
#endif

/// <summary>
/// Account Links are the means by which a Connect platform grants a connected account
Expand All @@ -18,26 +21,40 @@ public class AccountLink : StripeEntity<AccountLink>, IHasObject
/// String representing the object's type. Objects of the same type share the same value.
/// </summary>
[JsonProperty("object")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("object")]
#endif
public string Object { get; set; }

/// <summary>
/// Time at which the object was created. Measured in seconds since the Unix epoch.
/// </summary>
[JsonProperty("created")]
[JsonConverter(typeof(UnixDateTimeConverter))]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("created")]
[STJS.JsonConverter(typeof(STJUnixDateTimeConverter))]
#endif
public DateTime Created { get; set; } = Stripe.Infrastructure.DateTimeUtils.UnixEpoch;

/// <summary>
/// The timestamp at which this account link will expire.
/// </summary>
[JsonProperty("expires_at")]
[JsonConverter(typeof(UnixDateTimeConverter))]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("expires_at")]
[STJS.JsonConverter(typeof(STJUnixDateTimeConverter))]
#endif
public DateTime ExpiresAt { get; set; } = Stripe.Infrastructure.DateTimeUtils.UnixEpoch;

/// <summary>
/// The URL for the account link.
/// </summary>
[JsonProperty("url")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("url")]
#endif
public string Url { get; set; }
}
}
36 changes: 36 additions & 0 deletions src/Stripe.net/Entities/AccountNotices/AccountNotice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ namespace Stripe
using System.Collections.Generic;
using Newtonsoft.Json;
using Stripe.Infrastructure;
#if NET6_0_OR_GREATER
using STJS = System.Text.Json.Serialization;
#endif

/// <summary>
/// A notice to a Connected account. Notice can be sent by Stripe on your behalf or you can
Expand All @@ -20,45 +23,68 @@ public class AccountNotice : StripeEntity<AccountNotice>, IHasId, IHasMetadata,
/// Unique identifier for the object.
/// </summary>
[JsonProperty("id")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("id")]
#endif
public string Id { get; set; }

/// <summary>
/// String representing the object's type. Objects of the same type share the same value.
/// </summary>
[JsonProperty("object")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("object")]
#endif
public string Object { get; set; }

/// <summary>
/// Time at which the object was created. Measured in seconds since the Unix epoch.
/// </summary>
[JsonProperty("created")]
[JsonConverter(typeof(UnixDateTimeConverter))]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("created")]
[STJS.JsonConverter(typeof(STJUnixDateTimeConverter))]
#endif
public DateTime Created { get; set; } = Stripe.Infrastructure.DateTimeUtils.UnixEpoch;

/// <summary>
/// When present, the deadline for sending the notice to meet the relevant regulations.
/// </summary>
[JsonProperty("deadline")]
[JsonConverter(typeof(UnixDateTimeConverter))]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("deadline")]
[STJS.JsonConverter(typeof(STJUnixDateTimeConverter))]
#endif
public DateTime? Deadline { get; set; }

/// <summary>
/// Information about the email when sent.
/// </summary>
[JsonProperty("email")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("email")]
#endif
public AccountNoticeEmail Email { get; set; }

/// <summary>
/// Information about objects related to the notice.
/// </summary>
[JsonProperty("linked_objects")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("linked_objects")]
#endif
public AccountNoticeLinkedObjects LinkedObjects { get; set; }

/// <summary>
/// Has the value <c>true</c> if the object exists in live mode or the value <c>false</c> if
/// the object exists in test mode.
/// </summary>
[JsonProperty("livemode")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("livemode")]
#endif
public bool Livemode { get; set; }

/// <summary>
Expand All @@ -67,6 +93,9 @@ public class AccountNotice : StripeEntity<AccountNotice>, IHasId, IHasMetadata,
/// object in a structured format.
/// </summary>
[JsonProperty("metadata")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("metadata")]
#endif
public Dictionary<string, string> Metadata { get; set; }

/// <summary>
Expand All @@ -89,6 +118,9 @@ public class AccountNotice : StripeEntity<AccountNotice>, IHasId, IHasMetadata,
/// <c>issuing.dispute_won</c>.
/// </summary>
[JsonProperty("reason")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("reason")]
#endif
public string Reason { get; set; }

/// <summary>
Expand All @@ -97,6 +129,10 @@ public class AccountNotice : StripeEntity<AccountNotice>, IHasId, IHasMetadata,
/// </summary>
[JsonProperty("sent_at")]
[JsonConverter(typeof(UnixDateTimeConverter))]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("sent_at")]
[STJS.JsonConverter(typeof(STJUnixDateTimeConverter))]
#endif
public DateTime? SentAt { get; set; }
}
}
12 changes: 12 additions & 0 deletions src/Stripe.net/Entities/AccountNotices/AccountNoticeEmail.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
namespace Stripe
{
using Newtonsoft.Json;
#if NET6_0_OR_GREATER
using STJS = System.Text.Json.Serialization;
#endif

public class AccountNoticeEmail : StripeEntity<AccountNoticeEmail>
{
Expand All @@ -10,18 +13,27 @@ public class AccountNoticeEmail : StripeEntity<AccountNoticeEmail>
/// Compliance has approved for use.
/// </summary>
[JsonProperty("plain_text")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("plain_text")]
#endif
public string PlainText { get; set; }

/// <summary>
/// Email address of the recipient.
/// </summary>
[JsonProperty("recipient")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("recipient")]
#endif
public string Recipient { get; set; }

/// <summary>
/// Subject of the email.
/// </summary>
[JsonProperty("subject")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("subject")]
#endif
public string Subject { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
namespace Stripe
{
using Newtonsoft.Json;
#if NET6_0_OR_GREATER
using STJS = System.Text.Json.Serialization;
#endif

public class AccountNoticeLinkedObjects : StripeEntity<AccountNoticeLinkedObjects>
{
/// <summary>
/// Associated <a href="https://stripe.com/docs/api/capabilities">Capability</a>.
/// </summary>
[JsonProperty("capability")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("capability")]
#endif
public string Capability { get; set; }

/// <summary>
Expand All @@ -17,12 +23,18 @@ public class AccountNoticeLinkedObjects : StripeEntity<AccountNoticeLinkedObject
/// Underwriting Record</a>.
/// </summary>
[JsonProperty("issuing_credit_underwriting_record")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("issuing_credit_underwriting_record")]
#endif
public string IssuingCreditUnderwritingRecord { get; set; }

/// <summary>
/// Associated <a href="https://stripe.com/docs/api/issuing/disputes">Issuing Dispute</a>.
/// </summary>
[JsonProperty("issuing_dispute")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("issuing_dispute")]
#endif
public string IssuingDispute { get; set; }
}
}
22 changes: 22 additions & 0 deletions src/Stripe.net/Entities/AccountSessions/AccountSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ namespace Stripe
using System;
using Newtonsoft.Json;
using Stripe.Infrastructure;
#if NET6_0_OR_GREATER
using STJS = System.Text.Json.Serialization;
#endif

/// <summary>
/// An AccountSession allows a Connect platform to grant access to a connected account in
Expand All @@ -23,12 +26,18 @@ public class AccountSession : StripeEntity<AccountSession>, IHasObject
/// String representing the object's type. Objects of the same type share the same value.
/// </summary>
[JsonProperty("object")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("object")]
#endif
public string Object { get; set; }

/// <summary>
/// The ID of the account the AccountSession was created for.
/// </summary>
[JsonProperty("account")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("account")]
#endif
public string Account { get; set; }

/// <summary>
Expand All @@ -45,23 +54,36 @@ public class AccountSession : StripeEntity<AccountSession>, IHasObject
/// handled.
/// </summary>
[JsonProperty("client_secret")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("client_secret")]
#endif
public string ClientSecret { get; set; }

[JsonProperty("components")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("components")]
#endif
public AccountSessionComponents Components { get; set; }

/// <summary>
/// The timestamp at which this AccountSession will expire.
/// </summary>
[JsonProperty("expires_at")]
[JsonConverter(typeof(UnixDateTimeConverter))]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("expires_at")]
[STJS.JsonConverter(typeof(STJUnixDateTimeConverter))]
#endif
public DateTime ExpiresAt { get; set; } = Stripe.Infrastructure.DateTimeUtils.UnixEpoch;

/// <summary>
/// Has the value <c>true</c> if the object exists in live mode or the value <c>false</c> if
/// the object exists in test mode.
/// </summary>
[JsonProperty("livemode")]
#if NET6_0_OR_GREATER
[STJS.JsonPropertyName("livemode")]
#endif
public bool Livemode { get; set; }
}
}
Loading

0 comments on commit ba42f71

Please sign in to comment.