diff --git a/backend/src/Designer/Exceptions/Options/InvalidOptionsFormatException.cs b/backend/src/Designer/Exceptions/Options/InvalidOptionsFormatException.cs
new file mode 100644
index 00000000000..0dc69bc495d
--- /dev/null
+++ b/backend/src/Designer/Exceptions/Options/InvalidOptionsFormatException.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Altinn.Studio.Designer.Exceptions.Options;
+
+///
+/// Indicates that an error occurred during json serialization of options.
+///
+[Serializable]
+public class InvalidOptionsFormatException : Exception
+{
+ ///
+ public InvalidOptionsFormatException()
+ {
+ }
+
+ ///
+ public InvalidOptionsFormatException(string message) : base(message)
+ {
+ }
+
+ ///
+ public InvalidOptionsFormatException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+}
diff --git a/backend/src/Designer/Filters/Options/OptionsErrorCodes.cs b/backend/src/Designer/Filters/Options/OptionsErrorCodes.cs
new file mode 100644
index 00000000000..9053087b1b5
--- /dev/null
+++ b/backend/src/Designer/Filters/Options/OptionsErrorCodes.cs
@@ -0,0 +1,6 @@
+namespace Altinn.Studio.Designer.Filters.Options;
+
+public class OptionsErrorCodes
+{
+ public const string InvalidOptionsFormat = nameof(InvalidOptionsFormat);
+}
diff --git a/backend/src/Designer/Filters/Options/OptionsExceptionFilterAttribute.cs b/backend/src/Designer/Filters/Options/OptionsExceptionFilterAttribute.cs
new file mode 100644
index 00000000000..ead5e0a589a
--- /dev/null
+++ b/backend/src/Designer/Filters/Options/OptionsExceptionFilterAttribute.cs
@@ -0,0 +1,26 @@
+using System.Net;
+using Altinn.Studio.Designer.Exceptions.Options;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Controllers;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace Altinn.Studio.Designer.Filters.Options;
+
+public class OptionsExceptionFilterAttribute : ExceptionFilterAttribute
+{
+ public override void OnException(ExceptionContext context)
+ {
+ base.OnException(context);
+
+ if (context.ActionDescriptor is not ControllerActionDescriptor)
+ {
+ return;
+ }
+
+ if (context.Exception is InvalidOptionsFormatException)
+ {
+ context.Result = new ObjectResult(ProblemDetailsUtils.GenerateProblemDetails(context.Exception, OptionsErrorCodes.InvalidOptionsFormat, HttpStatusCode.BadRequest)) { StatusCode = (int)HttpStatusCode.BadRequest };
+ }
+ }
+
+}
diff --git a/backend/src/Designer/Helpers/JsonConverterHelpers/AllowEmptyStringAttribute.cs b/backend/src/Designer/Helpers/JsonConverterHelpers/AllowEmptyStringAttribute.cs
new file mode 100644
index 00000000000..b3a27993177
--- /dev/null
+++ b/backend/src/Designer/Helpers/JsonConverterHelpers/AllowEmptyStringAttribute.cs
@@ -0,0 +1,15 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Altinn.Studio.Designer.Helpers.JsonConverterHelpers;
+
+public class AllowEmptyStringAttribute : ValidationAttribute
+{
+ protected override ValidationResult IsValid(object value, ValidationContext validationContext)
+ {
+ if (value == null)
+ {
+ return new ValidationResult("The field is required.");
+ }
+ return ValidationResult.Success;
+ }
+}
diff --git a/backend/src/Designer/Helpers/JsonConverterHelpers/OptionConverterHelper.cs b/backend/src/Designer/Helpers/JsonConverterHelpers/OptionConverterHelper.cs
index a15ad94deeb..44a450866fd 100644
--- a/backend/src/Designer/Helpers/JsonConverterHelpers/OptionConverterHelper.cs
+++ b/backend/src/Designer/Helpers/JsonConverterHelpers/OptionConverterHelper.cs
@@ -1,6 +1,7 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
+using Altinn.Studio.Designer.Exceptions.Options;
namespace Altinn.Studio.Designer.Helpers.JsonConverterHelpers;
@@ -14,7 +15,7 @@ public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS
JsonTokenType.Number when reader.TryGetDouble(out double d) => d,
JsonTokenType.True => true,
JsonTokenType.False => false,
- _ => throw new JsonException($"Unsupported JSON token for Option.Value: {reader.TokenType}")
+ _ => throw new InvalidOptionsFormatException($"Unsupported JSON token for Option property, {typeToConvert}: {reader.TokenType}.")
};
}
@@ -32,7 +33,7 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp
writer.WriteBooleanValue(b);
break;
default:
- throw new JsonException("Unsupported type for Option.Value.");
+ throw new InvalidOptionsFormatException($"{value} is an unsupported type for Option fields.");
}
}
}
diff --git a/backend/src/Designer/Infrastructure/MvcConfiguration.cs b/backend/src/Designer/Infrastructure/MvcConfiguration.cs
index b135b5a35cc..990b05a5498 100644
--- a/backend/src/Designer/Infrastructure/MvcConfiguration.cs
+++ b/backend/src/Designer/Infrastructure/MvcConfiguration.cs
@@ -2,6 +2,7 @@
using Altinn.Studio.Designer.Filters.DataModeling;
using Altinn.Studio.Designer.Filters.Git;
using Altinn.Studio.Designer.Filters.IO;
+using Altinn.Studio.Designer.Filters.Options;
using Altinn.Studio.Designer.ModelBinding;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
@@ -26,6 +27,7 @@ public static IServiceCollection ConfigureMvc(this IServiceCollection services)
options.Filters.Add(typeof(DataModelingExceptionFilterAttribute));
options.Filters.Add(typeof(GitExceptionFilterAttribute));
options.Filters.Add(typeof(IoExceptionFilterAttribute));
+ options.Filters.Add(typeof(OptionsExceptionFilterAttribute));
})
.AddNewtonsoftJson(options => options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()));
diff --git a/backend/src/Designer/Models/Option.cs b/backend/src/Designer/Models/Option.cs
index ddfa6b9559e..697f72140c3 100644
--- a/backend/src/Designer/Models/Option.cs
+++ b/backend/src/Designer/Models/Option.cs
@@ -1,4 +1,3 @@
-using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using Altinn.Studio.Designer.Helpers.JsonConverterHelpers;
@@ -12,7 +11,7 @@ public class Option
///
/// Value that connects the option to the data model.
///
- [Required]
+ [AllowEmptyString]
[JsonPropertyName("value")]
[JsonConverter(typeof(OptionConverter))]
public object Value { get; set; }
@@ -20,7 +19,7 @@ public class Option
///
/// Label to present to the user.
///
- [Required]
+ [AllowEmptyString]
[JsonPropertyName("label")]
public string Label { get; set; }
diff --git a/backend/src/Designer/Services/Implementation/OptionsService.cs b/backend/src/Designer/Services/Implementation/OptionsService.cs
index f1834c3156a..9d4c36911db 100644
--- a/backend/src/Designer/Services/Implementation/OptionsService.cs
+++ b/backend/src/Designer/Services/Implementation/OptionsService.cs
@@ -3,6 +3,7 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using Altinn.Studio.Designer.Exceptions.Options;
using Altinn.Studio.Designer.Models;
using Altinn.Studio.Designer.Services.Interfaces;
using LibGit2Sharp;
@@ -73,10 +74,10 @@ public async Task> UploadNewOption(string org, string repo, string
List