diff --git a/src/Stripe.net/Infrastructure/JsonConverters/STJEmptyableConverter.cs b/src/Stripe.net/Infrastructure/JsonConverters/STJEmptyableConverter.cs
new file mode 100644
index 0000000000..bba884dfff
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/JsonConverters/STJEmptyableConverter.cs
@@ -0,0 +1,77 @@
+#if NET6_0_OR_GREATER
+namespace Stripe.Infrastructure
+{
+ using System;
+ using System.Reflection;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
+
+ ///
+ /// Converts a to and from JSON.
+ ///
+ /// Type of the field when expanded.
+ internal class STJEmptyableConverter : JsonConverter>
+ {
+ ///
+ /// Writes the JSON representation of the object.
+ ///
+ /// The to write to.
+ /// The value.
+ /// The calling serializer's options.
+ public override void Write(Utf8JsonWriter writer, Emptyable value, JsonSerializerOptions options)
+ {
+ switch (value)
+ {
+ case null:
+ break;
+
+ case Emptyable emptyable:
+ if (emptyable.Empty)
+ {
+ writer.WriteNullValue();
+ }
+ else
+ {
+ JsonSerializer.Serialize(writer, emptyable.Value, options);
+ }
+
+ break;
+
+ default:
+ throw new JsonException(string.Format(
+ "Unexpected value when converting Emptyable. Expected Emptyable, got {0}.",
+ value.GetType()));
+ }
+ }
+
+ ///
+ /// Determines whether this instance can convert the specified object type.
+ ///
+ /// Type of the object.
+ ///
+ /// true if this instance can convert the specified object type; otherwise, false.
+ ///
+ public override bool CanConvert(Type objectType)
+ {
+ if (!objectType.IsGenericType)
+ {
+ return false;
+ }
+
+ return typeof(IEmptyable).GetTypeInfo().IsAssignableFrom(objectType.GetGenericTypeDefinition());
+ }
+
+ ///
+ /// Reads the JSON representation of the object.
+ ///
+ /// The to read from.
+ /// Type of the object.
+ /// The calling serializer's options.
+ /// The object value.
+ public override Emptyable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotSupportedException("Cannot deserialize Emptyable objects.");
+ }
+ }
+}
+#endif
diff --git a/src/Stripe.net/Services/_common/Emptyable.cs b/src/Stripe.net/Services/_common/Emptyable.cs
index a0385d00f8..04da6f8583 100644
--- a/src/Stripe.net/Services/_common/Emptyable.cs
+++ b/src/Stripe.net/Services/_common/Emptyable.cs
@@ -1,8 +1,8 @@
namespace Stripe
{
- /// Represents a generic expandable field.
- /// Type of the field when expanded.
- internal class Emptyable
+ /// Represents a field that might be emptyble.
+ /// Type of the field when not empty.
+ internal class Emptyable : IEmptyable
{
private bool empty;
private T value;
@@ -29,5 +29,9 @@ public bool Empty
}
}
}
+
+ /// Gets or sets the expanded object.
+ /// The expanded object.
+ object IEmptyable.Value => this.Value;
}
}
diff --git a/src/Stripe.net/Services/_interfaces/IEmptyable.cs b/src/Stripe.net/Services/_interfaces/IEmptyable.cs
new file mode 100644
index 0000000000..26205777da
--- /dev/null
+++ b/src/Stripe.net/Services/_interfaces/IEmptyable.cs
@@ -0,0 +1,25 @@
+namespace Stripe
+{
+ ///
+ /// Represents a value that may be of one of several different types.
+ ///
+ public interface IEmptyable
+ {
+ /// Gets whether or not the field is empty.
+ /// True if the value is empty; false if the value is set.
+ public bool Empty { get; }
+
+ /// Gets the value of the current object.
+ /// The value of the current object.
+ object Value { get; }
+ }
+
+ /// Represents a generic expandable field.
+ /// Type of the field when expanded.
+ public interface IEmptyable : IEmptyable
+ {
+ /// Gets the value of the current object.
+ /// The value of the current object.
+ new T Value { get; set; }
+ }
+}
diff --git a/src/StripeTests/Wholesome/NewtonsoftAndSystemTextJsonOutputTheSameObject.cs b/src/StripeTests/Wholesome/NewtonsoftAndSystemTextJsonOutputTheSameObject.cs
index 3d6b2beedb..91cac63704 100644
--- a/src/StripeTests/Wholesome/NewtonsoftAndSystemTextJsonOutputTheSameObject.cs
+++ b/src/StripeTests/Wholesome/NewtonsoftAndSystemTextJsonOutputTheSameObject.cs
@@ -81,11 +81,6 @@ public void Check()
continue;
}
- if (stripeClass.Name.StartsWith("V1Billing"))
- {
- Debugger.Break();
- }
-
if (stripeClass.IsGenericType)
{
// Handle generic types (container types) separately, because
diff --git a/src/StripeTests/Wholesome/SystemTextJsonTestUtils.cs b/src/StripeTests/Wholesome/SystemTextJsonTestUtils.cs
index 4bf39965b6..21800921e9 100644
--- a/src/StripeTests/Wholesome/SystemTextJsonTestUtils.cs
+++ b/src/StripeTests/Wholesome/SystemTextJsonTestUtils.cs
@@ -115,6 +115,11 @@ public static Tuple HasCorrectConverterType(Type type, MemberInf
{
expectedConverterType = typeof(STJUnixDateTimeConverter);
}
+ else if (typeof(IEmptyable).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
+ {
+ expectedConverterType = typeof(STJEmptyableConverter<>);
+ expectedGenericTypeArguments = type.GenericTypeArguments;
+ }
else if (typeof(IAnyOf).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
{
expectedConverterType = typeof(STJAnyOfConverter);