diff --git a/src/Api/Swashbuckle.Owin.Tests/DotVVM.Framework.Api.Swashbuckle.Owin.Tests.csproj b/src/Api/Swashbuckle.Owin.Tests/DotVVM.Framework.Api.Swashbuckle.Owin.Tests.csproj index 24aafca696..c3da0a5ed1 100644 --- a/src/Api/Swashbuckle.Owin.Tests/DotVVM.Framework.Api.Swashbuckle.Owin.Tests.csproj +++ b/src/Api/Swashbuckle.Owin.Tests/DotVVM.Framework.Api.Swashbuckle.Owin.Tests.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/AutoUI/Core/Controls/BootstrapForm.cs b/src/AutoUI/Core/Controls/BootstrapForm.cs index fabc8210f4..e685d311f5 100644 --- a/src/AutoUI/Core/Controls/BootstrapForm.cs +++ b/src/AutoUI/Core/Controls/BootstrapForm.cs @@ -95,7 +95,7 @@ public static readonly DotvvmProperty FormCheckInputCssClassProperty /// public bool WrapControlInDiv { - get { return (bool)GetValue(WrapControlInDivProperty); } + get { return (bool)GetValue(WrapControlInDivProperty)!; } set { SetValue(WrapControlInDivProperty, value); } } public static readonly DotvvmProperty WrapControlInDivProperty diff --git a/src/Framework/Core/DotVVM.Core.csproj b/src/Framework/Core/DotVVM.Core.csproj index 76ef2cabbe..59d4af116a 100644 --- a/src/Framework/Core/DotVVM.Core.csproj +++ b/src/Framework/Core/DotVVM.Core.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/Framework/Core/Storage/ReturnedFile.cs b/src/Framework/Core/Storage/ReturnedFile.cs index f3e509f5e3..b56d9219be 100644 --- a/src/Framework/Core/Storage/ReturnedFile.cs +++ b/src/Framework/Core/Storage/ReturnedFile.cs @@ -18,6 +18,5 @@ public ReturnedFile(Stream stream, ReturnedFileMetadata metadata) Stream = stream; Metadata = metadata; } - } } diff --git a/src/Framework/Framework/Binding/BindingHelper.cs b/src/Framework/Framework/Binding/BindingHelper.cs index 855b87d4c1..eb1a4719b0 100644 --- a/src/Framework/Framework/Binding/BindingHelper.cs +++ b/src/Framework/Framework/Binding/BindingHelper.cs @@ -231,7 +231,7 @@ public static T GetCommandDelegate(this ICommandBinding binding, DotvvmBin /// /// Finds expected DataContext, gets the delegate from command binding and evaluates it with `args` /// - public static object? Evaluate(this ICommandBinding binding, DotvvmBindableObject control, params Func[] args) + public static object? Evaluate(this ICommandBinding binding, DotvvmBindableObject control, params Func[] args) { var action = binding.GetCommandDelegate(control); if (action is null) diff --git a/src/Framework/Framework/Binding/Expressions/BindingDebugJsonConverter.cs b/src/Framework/Framework/Binding/Expressions/BindingDebugJsonConverter.cs index a34f187eb1..753865b170 100644 --- a/src/Framework/Framework/Binding/Expressions/BindingDebugJsonConverter.cs +++ b/src/Framework/Framework/Binding/Expressions/BindingDebugJsonConverter.cs @@ -9,12 +9,12 @@ internal class BindingDebugJsonConverter: JsonConverter { public override bool CanConvert(Type objectType) => typeof(IBinding).IsAssignableFrom(objectType); - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) => throw new NotImplementedException("Deserializing dotvvm bindings from JSON is not supported."); - public override void WriteJson(JsonWriter w, object valueObj, JsonSerializer serializer) + public override void WriteJson(JsonWriter w, object? valueObj, JsonSerializer serializer) { var obj = valueObj; - w.WriteValue(obj.ToString()); + w.WriteValue(obj?.ToString()); // w.WriteStartObject(); // w.WritePropertyName("ToString"); diff --git a/src/Framework/Framework/Compilation/Binding/MemberExpressionFactory.cs b/src/Framework/Framework/Compilation/Binding/MemberExpressionFactory.cs index 9a14dae933..da6ac12ce7 100644 --- a/src/Framework/Framework/Compilation/Binding/MemberExpressionFactory.cs +++ b/src/Framework/Framework/Compilation/Binding/MemberExpressionFactory.cs @@ -247,8 +247,7 @@ public Expression CallMethod(Type target, BindingFlags flags, string name, Type[ if (b is null) throw new ArgumentNullException(nameof(b)); if (operatorName is null) throw new ArgumentNullException(nameof(b)); - var searchTypes = new [] { a.Type, b.Type, a.Type.UnwrapNullableType(), b.Type.UnwrapNullableType() }.OfType().Distinct().ToArray(); - + var searchTypes = new [] { a.Type, b.Type, a.Type.UnwrapNullableType(), b.Type.UnwrapNullableType() }.WhereNotNull().Distinct().ToArray(); // https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/expressions.md#1145-binary-operator-overload-resolution // The set of candidate user-defined operators provided by X and Y for the operation operator «op»(x, y) is determined. The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of §11.4.6. diff --git a/src/Framework/Framework/Compilation/Binding/StaticCommandExecutionPlanSerializer.cs b/src/Framework/Framework/Compilation/Binding/StaticCommandExecutionPlanSerializer.cs index e9165b8809..76b48dab73 100644 --- a/src/Framework/Framework/Compilation/Binding/StaticCommandExecutionPlanSerializer.cs +++ b/src/Framework/Framework/Compilation/Binding/StaticCommandExecutionPlanSerializer.cs @@ -35,7 +35,7 @@ public static JToken SerializePlan(StaticCommandInvocationPlan plan) } else if (arg.Type == StaticCommandParameterType.Constant) { - array.Add(JToken.FromObject(arg.Arg)); + array.Add(arg.Arg is null ? JValue.CreateNull() : JToken.FromObject(arg.Arg)); } else if (arg.Type == StaticCommandParameterType.DefaultValue) { @@ -54,7 +54,7 @@ public static JToken SerializePlan(StaticCommandInvocationPlan plan) } else throw new NotSupportedException(arg.Type.ToString()); } - while (array.Last.Type == JTokenType.Null) + while (array.Last!.Type == JTokenType.Null) array.Last.Remove(); return array; } @@ -84,19 +84,21 @@ public static string[] GetEncryptionPurposes() public static StaticCommandInvocationPlan DeserializePlan(JToken planInJson) { var jarray = (JArray)planInJson; - var typeName = jarray[0].Value(); - var methodName = jarray[1].Value(); - var genericTypeNames = jarray[2].Value(); - var parametersCount = jarray[3].Value(); - var parameterTypeNames = jarray[4].Value(); + if (jarray.Count < 6) + throw new NotSupportedException("Invalid static command plan."); + var typeName = jarray[0]!.Value()!; + var methodName = jarray[1]!.Value()!; + var genericTypeNames = jarray[2]!.Value(); + var parametersCount = jarray[3]!.Value(); + var parameterTypeNames = jarray[4]!.Value(); var hasOtherOverloads = parameterTypeNames != null; - var argTypes = jarray[5].ToObject().Select(a => (StaticCommandParameterType)a).ToArray(); + var argTypes = jarray[5]!.ToObject()!.Select(a => (StaticCommandParameterType)a).ToArray(); MethodInfo? method; if (hasOtherOverloads) { // There are multiple overloads available, therefore exact parameters need to be resolved first - var parameters = parameterTypeNames!.Select(n => Type.GetType(n.Value()).NotNull()).ToArray(); + var parameters = parameterTypeNames!.Select(n => Type.GetType(n.Value()!).NotNull()).ToArray(); method = Type.GetType(typeName)?.GetMethod(methodName, parameters); } else @@ -110,7 +112,7 @@ public static StaticCommandInvocationPlan DeserializePlan(JToken planInJson) if (method.IsGenericMethod) { - var generics = genericTypeNames.Select(n => Type.GetType(n.Value()).NotNull()).ToArray(); + var generics = genericTypeNames.NotNull().Select(n => Type.GetType(n.Value()!).NotNull()).ToArray(); method = method.MakeGenericMethod(generics); } @@ -123,11 +125,11 @@ public static StaticCommandInvocationPlan DeserializePlan(JToken planInJson) case StaticCommandParameterType.Argument: case StaticCommandParameterType.Inject: if (a.arg.Type == JTokenType.Null) - return new StaticCommandParameterPlan(a.type, a.parameter?.ParameterType ?? method.DeclaringType); + return new StaticCommandParameterPlan(a.type, a.parameter?.ParameterType ?? method.DeclaringType.NotNull()); else - return new StaticCommandParameterPlan(a.type, a.arg.Value().Apply(Type.GetType)); + return new StaticCommandParameterPlan(a.type, a.arg.Value()!.Apply(Type.GetType)); case StaticCommandParameterType.Constant: - return new StaticCommandParameterPlan(a.type, a.arg.ToObject(a.parameter?.ParameterType ?? method.DeclaringType)); + return new StaticCommandParameterPlan(a.type, a.arg.ToObject(a.parameter?.ParameterType ?? method.DeclaringType.NotNull())); case StaticCommandParameterType.DefaultValue: return new StaticCommandParameterPlan(a.type, a.parameter?.DefaultValue); case StaticCommandParameterType.Invocation: diff --git a/src/Framework/Framework/Configuration/HtmlTagAttributePair.cs b/src/Framework/Framework/Configuration/HtmlTagAttributePair.cs index 5d2371790b..b39343e3d3 100644 --- a/src/Framework/Framework/Configuration/HtmlTagAttributePair.cs +++ b/src/Framework/Framework/Configuration/HtmlTagAttributePair.cs @@ -41,12 +41,12 @@ public override bool Equals(object? obj) public class HtmlTagAttributePairToStringConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - writer.WriteValue(value.ToString()); + writer.WriteValue(value?.ToString()); } - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { var value = reader.ReadAsString(); if (value == null) diff --git a/src/Framework/Framework/Controls/DotvvmControlDebugJsonConverter.cs b/src/Framework/Framework/Controls/DotvvmControlDebugJsonConverter.cs index 837db97f88..e171b3d79a 100644 --- a/src/Framework/Framework/Controls/DotvvmControlDebugJsonConverter.cs +++ b/src/Framework/Framework/Controls/DotvvmControlDebugJsonConverter.cs @@ -14,10 +14,15 @@ internal class DotvvmControlDebugJsonConverter : JsonConverter public override bool CanConvert(Type objectType) => typeof(DotvvmBindableObject).IsAssignableFrom(objectType); - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) => throw new NotImplementedException("Deserializing dotvvm control from JSON is not supported."); - public override void WriteJson(JsonWriter w, object valueObj, JsonSerializer serializer) + public override void WriteJson(JsonWriter w, object? valueObj, JsonSerializer serializer) { + if (valueObj is null) + { + w.WriteNull(); + return; + } var obj = (DotvvmBindableObject)valueObj; w.WriteStartObject(); diff --git a/src/Framework/Framework/Diagnostics/JsonSizeAnalyzer.cs b/src/Framework/Framework/Diagnostics/JsonSizeAnalyzer.cs index 142a5cc3e9..5e176771be 100644 --- a/src/Framework/Framework/Diagnostics/JsonSizeAnalyzer.cs +++ b/src/Framework/Framework/Diagnostics/JsonSizeAnalyzer.cs @@ -38,7 +38,7 @@ AtomicSizeProfile analyzeToken(JToken token) return r; } case JTokenType.String: - return new (((string)token).Length + 2); + return new ((((string?)token)?.Length ?? 4) + 2); case JTokenType.Integer: // This should be the same as token.ToString().Length, but I didn't want to allocate the string unnecesarily return new((int)Math.Log10(Math.Abs((long)token) + 1) + 1); diff --git a/src/Framework/Framework/DotVVM.Framework.csproj b/src/Framework/Framework/DotVVM.Framework.csproj index 3d18a8544a..4993bcc7ee 100644 --- a/src/Framework/Framework/DotVVM.Framework.csproj +++ b/src/Framework/Framework/DotVVM.Framework.csproj @@ -43,18 +43,21 @@ - - + + - + + + + diff --git a/src/Framework/Framework/Hosting/AssemblySerializableList.cs b/src/Framework/Framework/Hosting/AssemblySerializableList.cs index b2519089b2..6a1099b9e7 100644 --- a/src/Framework/Framework/Hosting/AssemblySerializableList.cs +++ b/src/Framework/Framework/Hosting/AssemblySerializableList.cs @@ -1,5 +1,5 @@ using DotVVM.Framework.Compilation; -using Microsoft.Extensions.DependencyModel; +using DotVVM.Framework.Utils; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -28,7 +28,7 @@ public static AssemblySerializableList CreateFromCache(CompiledAssemblyCache cac if (mainAssembly is {}) assemblies.Add(mainAssembly); return new AssemblySerializableList( - assemblyDirs: assemblies.Select(a => IO.Path.GetDirectoryName(a.Location)).OfType().Distinct().OrderBy(x => x).ToArray(), + assemblyDirs: assemblies.Select(a => IO.Path.GetDirectoryName(a.Location)).WhereNotNull().Distinct().OrderBy(x => x).ToArray(), assemblyNames: assemblies.Select(getName).OrderBy(x => x).ToArray()!, mainAssembly: getName(mainAssembly) ); diff --git a/src/Framework/Framework/Hosting/DotvvmPresenter.cs b/src/Framework/Framework/Hosting/DotvvmPresenter.cs index 5057cb515f..755cab055c 100644 --- a/src/Framework/Framework/Hosting/DotvvmPresenter.cs +++ b/src/Framework/Framework/Hosting/DotvvmPresenter.cs @@ -350,18 +350,18 @@ public async Task ProcessStaticCommandRequest(IDotvvmRequestContext context) } // validate csrf token - context.CsrfToken = postData["$csrfToken"].Value(); + context.CsrfToken = (postData["$csrfToken"]?.Value()).NotNull("$csrfToken is required"); CsrfProtector.VerifyToken(context, context.CsrfToken); - var knownTypes = postData["knownTypeMetadata"].Values().ToArray(); + var knownTypes = postData["knownTypeMetadata"]?.Values().WhereNotNull().ToArray() ?? Array.Empty(); var argumentPaths = postData["argumentPaths"]?.Values().ToArray(); - var command = postData["command"].Value(); - var arguments = postData["args"] as JArray; + var command = (postData["command"]?.Value()).NotNull("command is required"); + var arguments = (postData["args"] as JArray).NotNull("args is required"); var executionPlan = StaticCommandExecutor.DecryptPlan(command); var actionInfo = new ActionInfo( binding: null, - () => { return StaticCommandExecutor.Execute(executionPlan, arguments.NotNull(), argumentPaths, context); }, + () => { return StaticCommandExecutor.Execute(executionPlan, arguments, argumentPaths, context); }, false, executionPlan.Method, argumentPaths diff --git a/src/Framework/Framework/Hosting/ErrorPages/ErrorPageTemplate.cs b/src/Framework/Framework/Hosting/ErrorPages/ErrorPageTemplate.cs index 0286da5d4e..4d30b52d1a 100644 --- a/src/Framework/Framework/Hosting/ErrorPages/ErrorPageTemplate.cs +++ b/src/Framework/Framework/Hosting/ErrorPages/ErrorPageTemplate.cs @@ -154,6 +154,12 @@ public string TransformText() public void ObjectBrowser(object? obj) { + if (obj is null) + { + WriteText("null"); + return; + } + var settings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore, @@ -227,7 +233,11 @@ public void ObjectBrowser(JObject obj) Write("
"); WriteText(p.Key); Write(":"); - if (p.Value is JObject) + if (p.Value is null) + { + WriteText("null"); + } + else if (p.Value is JObject) { ObjectBrowser((JObject)p.Value); } @@ -360,9 +370,9 @@ class IgnoreStuffJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) => objectType.IsDelegate(); - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) => throw new NotImplementedException(); - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { writer.WriteValue(""); } diff --git a/src/Framework/Framework/Hosting/ModelState.cs b/src/Framework/Framework/Hosting/ModelState.cs index d7a195e489..03e685895e 100644 --- a/src/Framework/Framework/Hosting/ModelState.cs +++ b/src/Framework/Framework/Hosting/ModelState.cs @@ -13,7 +13,7 @@ public class ModelState /// /// Gets the validation target path relative to the command target. /// - internal string ValidationTargetPath { get; set; } + internal string? ValidationTargetPath { get; set; } /// /// Gets the object that was validated. diff --git a/src/Framework/Framework/ResourceManagement/ClientGlobalize/JQueryGlobalizeScriptCreator.cs b/src/Framework/Framework/ResourceManagement/ClientGlobalize/JQueryGlobalizeScriptCreator.cs index 4414b52f55..795265e657 100644 --- a/src/Framework/Framework/ResourceManagement/ClientGlobalize/JQueryGlobalizeScriptCreator.cs +++ b/src/Framework/Framework/ResourceManagement/ClientGlobalize/JQueryGlobalizeScriptCreator.cs @@ -133,11 +133,11 @@ private static JObject CreateNumberInfoJson(NumberFormatInfo ni) var jobj = JObject.FromObject(numberFormat); jobj[","] = ni.NumberGroupSeparator; jobj["."] = ni.NumberDecimalSeparator; - jobj["percent"][","] = ni.PercentGroupSeparator; - jobj["percent"]["."] = ni.PercentDecimalSeparator; + jobj["percent"]![","] = ni.PercentGroupSeparator; + jobj["percent"]!["."] = ni.PercentDecimalSeparator; - jobj["currency"][","] = ni.CurrencyGroupSeparator; - jobj["currency"]["."] = ni.CurrencyDecimalSeparator; + jobj["currency"]![","] = ni.CurrencyGroupSeparator; + jobj["currency"]!["."] = ni.CurrencyDecimalSeparator; return jobj; } diff --git a/src/Framework/Framework/ResourceManagement/ReflectionAssemblyJsonConverter.cs b/src/Framework/Framework/ResourceManagement/ReflectionAssemblyJsonConverter.cs index c3d90e3a74..18d89d6cac 100644 --- a/src/Framework/Framework/ResourceManagement/ReflectionAssemblyJsonConverter.cs +++ b/src/Framework/Framework/ResourceManagement/ReflectionAssemblyJsonConverter.cs @@ -13,7 +13,7 @@ public class ReflectionAssemblyJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) => typeof(Assembly).IsAssignableFrom(objectType); - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.Value is string name) { @@ -22,9 +22,9 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist else throw new NotSupportedException(); } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - writer.WriteValue(((Assembly)value).GetName().ToString()); + writer.WriteValue(((Assembly?)value)?.GetName().ToString()); } } @@ -32,7 +32,7 @@ public class ReflectionTypeJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) => typeof(Type).IsAssignableFrom(objectType); - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.Value is string name) { @@ -41,8 +41,14 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist else throw new NotSupportedException(); } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { + if (value is null) + { + writer.WriteNull(); + return; + } + var t = ((Type)value); if (t.Assembly == typeof(string).Assembly) writer.WriteValue(t.FullName); @@ -54,7 +60,7 @@ public class DotvvmTypeDescriptorJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) => typeof(ITypeDescriptor).IsAssignableFrom(objectType); - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.Value is string name) { @@ -63,8 +69,13 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist else throw new NotSupportedException(); } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { + if (value is null) + { + writer.WriteNull(); + return; + } var t = ((ITypeDescriptor)value); var coreAssembly = typeof(string).Assembly.GetName().Name; var assembly = t.Assembly?.Split(new char[] { ',' }, 2)[0]; diff --git a/src/Framework/Framework/ResourceManagement/ResourceRepositoryJsonConverter.cs b/src/Framework/Framework/ResourceManagement/ResourceRepositoryJsonConverter.cs index 24839da0c1..894a5b9d11 100644 --- a/src/Framework/Framework/ResourceManagement/ResourceRepositoryJsonConverter.cs +++ b/src/Framework/Framework/ResourceManagement/ResourceRepositoryJsonConverter.cs @@ -34,7 +34,7 @@ public override bool CanConvert(Type objectType) return null; } - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { var jobj = JObject.Load(reader); var repo = existingValue as DotvvmResourceRepository ?? new DotvvmResourceRepository(); @@ -42,15 +42,15 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist { if (resourceTypeAliases.FirstOrDefault(x => x.name == prop.Key) is var r && r.type != null) { - DeserializeResources((JObject)prop.Value, r.type, serializer, repo); + DeserializeResources((JObject)prop.Value.NotNull(), r.type, serializer, repo); } else if (CompiledAssemblyCache.Instance!.FindType(prop.Key) is Type resourceType) { - DeserializeResources((JObject)prop.Value, resourceType, serializer, repo); + DeserializeResources((JObject)prop.Value.NotNull(), resourceType, serializer, repo); } else if (UnknownResourceType != null) { - DeserializeResources((JObject)prop.Value, UnknownResourceType, serializer, repo); + DeserializeResources((JObject)prop.Value.NotNull(), UnknownResourceType, serializer, repo); } else throw new NotSupportedException(string.Format("resource collection name {0} is not supported", prop.Key)); @@ -64,7 +64,7 @@ void DeserializeResources(JObject jobj, Type resourceType, JsonSerializer serial { try { - var resource = (IResource)serializer.Deserialize(resObj.Value.CreateReader(), resourceType); + var resource = (IResource)serializer.Deserialize(resObj.Value!.CreateReader(), resourceType).NotNull(); if (resource is LinkResourceBase linkResource) { if (linkResource.Location == null) @@ -82,8 +82,13 @@ void DeserializeResources(JObject jobj, Type resourceType, JsonSerializer serial } } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { + if (value == null) + { + writer.WriteNull(); + return; + } writer.WriteStartObject(); var resources = value as DotvvmResourceRepository ?? throw new NotSupportedException(); foreach (var (name, group) in ( @@ -110,8 +115,8 @@ orderby name public class DeserializationErrorResource : ResourceBase { public Exception Error { get; } - public JToken Json { get; set; } - public DeserializationErrorResource(Exception error, JToken json) : base(ResourceRenderPosition.Head) + public JToken? Json { get; set; } + public DeserializationErrorResource(Exception error, JToken? json) : base(ResourceRenderPosition.Head) { this.Error = error; this.Json = json; @@ -119,7 +124,7 @@ public DeserializationErrorResource(Exception error, JToken json) : base(Resourc public override void Render(IHtmlWriter writer, IDotvvmRequestContext context, string resourceName) { - throw new NotSupportedException($"Resource could not be deserialized from '{Json.ToString()}': \n{Error}"); + throw new NotSupportedException($"Resource could not be deserialized from '{(Json is null ? "null" : Json.ToString())}': \n{Error}"); } } } diff --git a/src/Framework/Framework/Routing/RouteTableJsonConverter.cs b/src/Framework/Framework/Routing/RouteTableJsonConverter.cs index 0cce01e1fa..bd3e6154fe 100644 --- a/src/Framework/Framework/Routing/RouteTableJsonConverter.cs +++ b/src/Framework/Framework/Routing/RouteTableJsonConverter.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using DotVVM.Framework.Hosting; using System.Diagnostics.CodeAnalysis; +using DotVVM.Framework.Utils; namespace DotVVM.Framework.Routing { @@ -15,29 +16,34 @@ public class RouteTableJsonConverter : JsonConverter { public override bool CanConvert(Type objectType) => objectType == typeof(DotvvmRouteTable); - public override object? ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { var rt = existingValue as DotvvmRouteTable; if (rt == null) return null; foreach (var prop in (JObject)JObject.ReadFrom(reader)) { - var route = (JObject)prop.Value; + var route = (JObject)prop.Value.NotNull(); try { - rt.Add(prop.Key, route["url"].Value(), route["virtualPath"].Value(), route["defaultValues"].ToObject>()); + rt.Add(prop.Key, route["url"].NotNull("route.url is required").Value(), (route["virtualPath"]?.Value()).NotNull("route.virtualPath is required"), route["defaultValues"]?.ToObject>()); } catch (Exception error) { - rt.Add(prop.Key, new ErrorRoute(route["url"].Value(), route["virtualPath"].Value(), prop.Key, route["defaultValues"].ToObject>(), error)); + rt.Add(prop.Key, new ErrorRoute(route["url"]?.Value(), route["virtualPath"]?.Value(), prop.Key, route["defaultValues"]?.ToObject>(), error)); } } return rt; } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => WriteJson(writer, (DotvvmRouteTable)value, serializer); + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) => WriteJson(writer, (DotvvmRouteTable?)value, serializer); - public void WriteJson(JsonWriter writer, DotvvmRouteTable value, JsonSerializer serializer) + public void WriteJson(JsonWriter writer, DotvvmRouteTable? value, JsonSerializer serializer) { + if (value == null) + { + writer.WriteNull(); + return; + } writer.WriteStartObject(); foreach (var route in value) { @@ -55,7 +61,8 @@ sealed class ErrorRoute : RouteBase { private readonly Exception error; - public ErrorRoute(string url, string virtualPath, string name, IDictionary? defaultValues, Exception error) : base(url, virtualPath, name, defaultValues) + public ErrorRoute(string? url, string? virtualPath, string? name, IDictionary? defaultValues, Exception error) + : base(url ?? "", virtualPath ?? "", name ?? "", defaultValues) { this.error = error; } diff --git a/src/Framework/Framework/Runtime/Commands/CommandResolver.cs b/src/Framework/Framework/Runtime/Commands/CommandResolver.cs index 515888da4d..4936fbfcef 100644 --- a/src/Framework/Framework/Runtime/Commands/CommandResolver.cs +++ b/src/Framework/Framework/Runtime/Commands/CommandResolver.cs @@ -16,7 +16,7 @@ public class CommandResolver /// /// Resolves the command called on the DotvvmControl. /// - public ActionInfo GetFunction(DotvvmControl? targetControl, DotvvmControl viewRootControl, IDotvvmRequestContext context, string[] path, string commandId, Func[] args) + public ActionInfo GetFunction(DotvvmControl? targetControl, DotvvmControl viewRootControl, IDotvvmRequestContext context, string[] path, string commandId, Func[] args) { // event validation var validationTargetPath = context.ModelState.ValidationTargetPath; @@ -51,7 +51,7 @@ public ActionInfo GetFunction(DotvvmControl? targetControl, DotvvmControl viewRo /// /// Resolves the command called on the ViewModel. /// - public ActionInfo GetFunction(DotvvmControl viewRootControl, IDotvvmRequestContext context, string[] path, string command, Func[] args) + public ActionInfo GetFunction(DotvvmControl viewRootControl, IDotvvmRequestContext context, string[] path, string command, Func[] args) { return GetFunction(null, viewRootControl, context, path, command, args); } diff --git a/src/Framework/Framework/Runtime/Commands/EventValidator.cs b/src/Framework/Framework/Runtime/Commands/EventValidator.cs index f91467d556..d0cbfe1ba7 100644 --- a/src/Framework/Framework/Runtime/Commands/EventValidator.cs +++ b/src/Framework/Framework/Runtime/Commands/EventValidator.cs @@ -19,7 +19,7 @@ public class EventValidator /// /// Validates the command. /// - public FindBindingResult ValidateCommand(string[] path, string commandId, DotvvmControl viewRootControl, string validationTargetPath) + public FindBindingResult ValidateCommand(string[] path, string commandId, DotvvmControl viewRootControl, string? validationTargetPath) { // find the binding var result = FindCommandBinding(path, commandId, viewRootControl, validationTargetPath); @@ -175,13 +175,13 @@ private FindBindingResult FindCommandBinding(string[] path, string commandId, /// Finds the binding of the specified type on the specified viewmodel path. /// private FindBindingResult FindCommandBinding(string[] path, string commandId, - DotvvmBindableObject viewRootControl, string validationTargetPath) + DotvvmBindableObject viewRootControl, string? validationTargetPath) => FindCommandBinding(path, commandId, viewRootControl, null, validationTargetPath, false); /// /// Validates the control command. /// - public FindBindingResult ValidateControlCommand(string[] path, string commandId, DotvvmControl viewRootControl, DotvvmControl targetControl, string validationTargetPath) + public FindBindingResult ValidateControlCommand(string[] path, string commandId, DotvvmControl viewRootControl, DotvvmControl targetControl, string? validationTargetPath) { // find the binding var result = FindControlCommandBinding(path, commandId, viewRootControl, targetControl, validationTargetPath); @@ -196,7 +196,7 @@ public FindBindingResult ValidateControlCommand(string[] path, string commandId, /// Finds the binding of the specified type on the specified viewmodel path. /// private FindBindingResult FindControlCommandBinding(string[] path, string commandId, - DotvvmControl viewRootControl, DotvvmBindableObject targetControl, string validationTargetPath) + DotvvmControl viewRootControl, DotvvmBindableObject targetControl, string? validationTargetPath) => FindCommandBinding(path, commandId, viewRootControl, targetControl, validationTargetPath, true); diff --git a/src/Framework/Framework/Storage/FileSystemReturnedFileStorage.cs b/src/Framework/Framework/Storage/FileSystemReturnedFileStorage.cs index 647193eca2..3ab0ab2d93 100644 --- a/src/Framework/Framework/Storage/FileSystemReturnedFileStorage.cs +++ b/src/Framework/Framework/Storage/FileSystemReturnedFileStorage.cs @@ -111,7 +111,7 @@ public Task GetFileAsync(Guid id) { var metadataJson = File.ReadAllText(GetMetadataFilePath(id), Encoding.UTF8); var settings = DefaultSerializerSettingsProvider.Instance.Settings; - var metadata = JsonConvert.DeserializeObject(metadataJson, settings); + var metadata = JsonConvert.DeserializeObject(metadataJson, settings).NotNull(); var stream = new FileStream(GetDataFilePath(id), FileMode.Open, FileAccess.Read); diff --git a/src/Framework/Framework/Utils/FunctionalExtensions.cs b/src/Framework/Framework/Utils/FunctionalExtensions.cs index e2b08413b7..7dd383c860 100644 --- a/src/Framework/Framework/Utils/FunctionalExtensions.cs +++ b/src/Framework/Framework/Utils/FunctionalExtensions.cs @@ -107,6 +107,17 @@ public static T NotNull([NotNull] this T? target, string message = "Unexpecte public static SortedDictionary ToSorted(this IDictionary d, IComparer? c = null) where K: notnull => new(d, c ?? Comparer.Default); + + internal static IEnumerable WhereNotNull(this IEnumerable enumerable) where T : class => + enumerable.Where(x => x != null)!; + internal static IEnumerable WhereNotNull(this IEnumerable> enumerable) where T : struct + { + foreach (var x in enumerable) + { + if (x.HasValue) + yield return x.Value; + } + } } sealed class ObjectWithComparer : IEquatable>, IEquatable diff --git a/src/Framework/Framework/Utils/JsonUtils.cs b/src/Framework/Framework/Utils/JsonUtils.cs index 95100cb41e..09bcbae835 100644 --- a/src/Framework/Framework/Utils/JsonUtils.cs +++ b/src/Framework/Framework/Utils/JsonUtils.cs @@ -56,7 +56,7 @@ public static JObject Diff(JObject source, JObject target, bool nullOnRemoved = else { var targetJson = $@"{{""Time"": ""{item.Value}""}}"; - targetTime = JObject.Parse(targetJson)["Time"].ToObject(); + targetTime = JObject.Parse(targetJson)["Time"]!.ToObject(); } if (!sourceTime.Equals(targetTime)) @@ -174,7 +174,7 @@ public static void Patch(JObject target, JObject diff, bool removeOnNull = false { var val = target[prop.Key]; if (val == null) target[prop.Key] = prop.Value; - else if (prop.Value.Type == JTokenType.Null && removeOnNull || (prop.Value as JConstructor)?.Name == "$rm") target.Remove(prop.Key); + else if (prop.Value!.Type == JTokenType.Null && removeOnNull || (prop.Value as JConstructor)?.Name == "$rm") target.Remove(prop.Key); else target[prop.Key] = PatchItem(val, prop.Value, removeOnNull); } } diff --git a/src/Framework/Framework/Utils/ReflectionUtils.cs b/src/Framework/Framework/Utils/ReflectionUtils.cs index 1b26d35c02..bf5e8ea0ef 100644 --- a/src/Framework/Framework/Utils/ReflectionUtils.cs +++ b/src/Framework/Framework/Utils/ReflectionUtils.cs @@ -555,7 +555,7 @@ public static Type GetResultType(this MemberInfo member) => } else if (EnumInfo.IsFlags) { - return JsonConvert.DeserializeObject(JsonConvert.ToString(instance.Value)); + return JsonConvert.DeserializeObject(JsonConvert.ToString(instance.Value))!; } else { diff --git a/src/Framework/Framework/ViewModel/Serialization/CustomPrimitiveTypeJsonConverter.cs b/src/Framework/Framework/ViewModel/Serialization/CustomPrimitiveTypeJsonConverter.cs index 377bf56a5d..7529850f24 100644 --- a/src/Framework/Framework/ViewModel/Serialization/CustomPrimitiveTypeJsonConverter.cs +++ b/src/Framework/Framework/ViewModel/Serialization/CustomPrimitiveTypeJsonConverter.cs @@ -15,7 +15,7 @@ public override bool CanConvert(Type objectType) return ReflectionUtils.IsCustomPrimitiveType(objectType); } - public override object? ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.TokenType is JsonToken.String or JsonToken.Boolean @@ -41,7 +41,7 @@ or JsonToken.Float } } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value == null) { diff --git a/src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs b/src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs index 3580e99997..dddffd8e26 100644 --- a/src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs +++ b/src/Framework/Framework/ViewModel/Serialization/DefaultViewModelSerializer.cs @@ -61,9 +61,9 @@ public string SerializeViewModel(IDotvvmRequestContext context) var timer = ValueStopwatch.StartNew(); context.ViewModelJson ??= new JObject(); - if (SendDiff && context.ReceivedViewModelJson != null && context.ViewModelJson["viewModel"] != null) + if (SendDiff && context.ReceivedViewModelJson?["viewModel"] is JObject receivedVM && context.ViewModelJson["viewModel"] is JObject responseVM) { - context.ViewModelJson["viewModelDiff"] = JsonUtils.Diff((JObject)context.ReceivedViewModelJson["viewModel"], (JObject)context.ViewModelJson["viewModel"], false, i => ShouldIncludeProperty(i.TypeId, i.Property)); + context.ViewModelJson["viewModelDiff"] = JsonUtils.Diff(receivedVM, responseVM, false, i => ShouldIncludeProperty(i.TypeId, i.Property)); context.ViewModelJson.Remove("viewModel"); } var result = context.ViewModelJson.ToString(JsonFormatting); @@ -117,7 +117,7 @@ public void BuildViewModel(IDotvvmRequestContext context, object? commandResult) { throw new SerializationException(true, context.ViewModel!.GetType(), writer.Path, ex); } - var viewModelToken = writer.Token; + var viewModelToken = writer.Token.NotNull(); string? viewModelCacheId = null; if (context.Configuration.ExperimentalFeatures.ServerSideViewModelCache.IsEnabledForRoute(context.Route?.RouteName)) @@ -177,13 +177,13 @@ public void BuildViewModel(IDotvvmRequestContext context, object? commandResult) private JObject SerializeTypeMetadata(IDotvvmRequestContext context, ViewModelJsonConverter viewModelJsonConverter) { - var knownTypeIds = context.ReceivedViewModelJson?["knownTypeMetadata"]?.Values().ToImmutableHashSet(); + var knownTypeIds = context.ReceivedViewModelJson?["knownTypeMetadata"]?.Values().WhereNotNull().ToImmutableHashSet(); return viewModelTypeMetadataSerializer.SerializeTypeMetadata(viewModelJsonConverter.UsedSerializationMaps, knownTypeIds); } public void AddNewResources(IDotvvmRequestContext context) { - var renderedResources = new HashSet(context.ReceivedViewModelJson?["renderedResources"]?.Values() ?? new string[] { }); + var renderedResources = new HashSet(context.ReceivedViewModelJson?["renderedResources"]?.Values().WhereNotNull() ?? new string[] { }); var resourcesObject = BuildResourcesJson(context, rn => !renderedResources.Contains(rn)); if (resourcesObject.Count > 0) context.ViewModelJson!["resources"] = resourcesObject; @@ -235,7 +235,7 @@ private static JToken WriteCommandData(object? data, JsonSerializer serializer, { throw new SerializationException(true, data?.GetType(), writer.Path, ex); } - return writer.Token; + return writer.Token.NotNull(); } protected virtual JsonSerializer CreateJsonSerializer() => DefaultSerializerSettingsProvider.Instance.Settings.Apply(JsonSerializer.Create); @@ -333,35 +333,34 @@ public void PopulateViewModel(IDotvvmRequestContext context, string serializedPo // get properties var data = context.ReceivedViewModelJson = JObject.Parse(serializedPostData); JObject viewModelToken; - if (data["viewModelCacheId"] != null) + if (data["viewModelCacheId"]?.Value() is string viewModelCacheId) { if (!context.Configuration.ExperimentalFeatures.ServerSideViewModelCache.IsEnabledForRoute(context.Route?.RouteName)) { throw new InvalidOperationException("The server-side viewmodel caching is not enabled for the current route!"); } - viewModelToken = viewModelServerCache.TryRestoreViewModel(context, (string)data["viewModelCacheId"], (JObject)data["viewModelDiff"]); + viewModelToken = viewModelServerCache.TryRestoreViewModel(context, viewModelCacheId, (JObject)data["viewModelDiff"]!); data["viewModel"] = viewModelToken; } else { - viewModelToken = (JObject)data["viewModel"]; + viewModelToken = (JObject)data["viewModel"]!; } // load CSRF token context.CsrfToken = viewModelToken["$csrfToken"]?.Value(); ViewModelJsonConverter viewModelConverter; - if (viewModelToken["$encryptedValues"] != null) + if (viewModelToken["$encryptedValues"]?.Value() is string encryptedValuesString) { // load encrypted values - var encryptedValuesString = viewModelToken["$encryptedValues"].Value(); viewModelConverter = new ViewModelJsonConverter(IsPostBack(context), viewModelMapper, context.Services, JObject.Parse(viewModelProtector.Unprotect(encryptedValuesString, context))); } else viewModelConverter = new ViewModelJsonConverter(IsPostBack(context), viewModelMapper, context.Services); // get validation path - context.ModelState.ValidationTargetPath = (string)data["validationTargetPath"]; + context.ModelState.ValidationTargetPath = (string?)data["validationTargetPath"]; // populate the ViewModel var serializer = CreateJsonSerializer(); @@ -390,12 +389,12 @@ public void PopulateViewModel(IDotvvmRequestContext context, string serializedPo { // get properties var data = context.ReceivedViewModelJson ?? throw new NotSupportedException("Could not find ReceivedViewModelJson in request context."); - var path = data["currentPath"].Values().ToArray(); - var command = data["command"].Value(); + var path = data["currentPath"].NotNull("currentPath is required").Values().ToArray(); + var command = data["command"].NotNull("command is required").Value(); var controlUniqueId = data["controlUniqueId"]?.Value(); var args = data["commandArgs"] is JArray argsJson ? - argsJson.Select(a => (Func)(t => a.ToObject(t))).ToArray() : - new Func[0]; + argsJson.Select(a => (Func)(t => a.ToObject(t))).ToArray() : + new Func[0]; // empty command if (string.IsNullOrEmpty(command)) return null; @@ -408,11 +407,11 @@ public void PopulateViewModel(IDotvvmRequestContext context, string serializedPo { throw new Exception(string.Format("The control with ID '{0}' was not found!", controlUniqueId)); } - return commandResolver.GetFunction(target, view, context, path, command, args); + return commandResolver.GetFunction(target, view, context, path!, command, args); } else { - return commandResolver.GetFunction(view, context, path, command, args); + return commandResolver.GetFunction(view, context, path!, command, args); } } diff --git a/src/Framework/Framework/ViewModel/Serialization/DotvvmByteArrayConverter.cs b/src/Framework/Framework/ViewModel/Serialization/DotvvmByteArrayConverter.cs index 9451357cb7..4df3b330c0 100644 --- a/src/Framework/Framework/ViewModel/Serialization/DotvvmByteArrayConverter.cs +++ b/src/Framework/Framework/ViewModel/Serialization/DotvvmByteArrayConverter.cs @@ -9,7 +9,7 @@ namespace DotVVM.Framework.ViewModel.Serialization { public class DotvvmByteArrayConverter : JsonConverter { - public override object? ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) { @@ -40,7 +40,7 @@ public class DotvvmByteArrayConverter : JsonConverter } } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value == null) { diff --git a/src/Framework/Framework/ViewModel/Serialization/DotvvmDateOnlyConverter.cs b/src/Framework/Framework/ViewModel/Serialization/DotvvmDateOnlyConverter.cs index be84dcb8b3..f7a138e4da 100644 --- a/src/Framework/Framework/ViewModel/Serialization/DotvvmDateOnlyConverter.cs +++ b/src/Framework/Framework/ViewModel/Serialization/DotvvmDateOnlyConverter.cs @@ -6,7 +6,7 @@ namespace DotVVM.Framework.ViewModel.Serialization { public class DotvvmDateOnlyConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value == null) { @@ -35,10 +35,10 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s } else if (reader.TokenType == JsonToken.Date) { - return (DateOnly)reader.Value; + return (DateOnly)reader.Value!; } else if (reader.TokenType == JsonToken.String - && DateOnly.TryParseExact((string)reader.Value, "O", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) + && DateOnly.TryParseExact((string)reader.Value!, "O", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) { return date; } diff --git a/src/Framework/Framework/ViewModel/Serialization/DotvvmDateTimeConverter.cs b/src/Framework/Framework/ViewModel/Serialization/DotvvmDateTimeConverter.cs index 381684bac5..daff902202 100644 --- a/src/Framework/Framework/ViewModel/Serialization/DotvvmDateTimeConverter.cs +++ b/src/Framework/Framework/ViewModel/Serialization/DotvvmDateTimeConverter.cs @@ -6,7 +6,7 @@ namespace DotVVM.Framework.ViewModel.Serialization { public class DotvvmDateTimeConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value == null) { @@ -35,10 +35,10 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s } else if (reader.TokenType == JsonToken.Date) { - return (DateTime) reader.Value; + return (DateTime) reader.Value!; } else if (reader.TokenType == JsonToken.String - && DateTime.TryParseExact((string)reader.Value, "O", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) + && DateTime.TryParseExact((string)reader.Value!, "O", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) { return date; } diff --git a/src/Framework/Framework/ViewModel/Serialization/DotvvmDictionaryConverter.cs b/src/Framework/Framework/ViewModel/Serialization/DotvvmDictionaryConverter.cs index ecd16b9500..946d20dca2 100644 --- a/src/Framework/Framework/ViewModel/Serialization/DotvvmDictionaryConverter.cs +++ b/src/Framework/Framework/ViewModel/Serialization/DotvvmDictionaryConverter.cs @@ -16,10 +16,9 @@ public class DotvvmDictionaryConverter : JsonConverter private static Type keyValuePairGenericType = typeof(KeyValuePair<,>); private static Type listGenericType = typeof(List<>); private static Type dictionaryEntryType = typeof(DictionaryEntry); - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - var dict = value as IDictionary; - if (dict == null) + if (value is not IDictionary dict) { writer.WriteNull(); } @@ -46,7 +45,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s } } - public override object? ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) { diff --git a/src/Framework/Framework/ViewModel/Serialization/DotvvmTimeOnlyConverter.cs b/src/Framework/Framework/ViewModel/Serialization/DotvvmTimeOnlyConverter.cs index 2b4a51df19..2a03f596c3 100644 --- a/src/Framework/Framework/ViewModel/Serialization/DotvvmTimeOnlyConverter.cs +++ b/src/Framework/Framework/ViewModel/Serialization/DotvvmTimeOnlyConverter.cs @@ -6,7 +6,7 @@ namespace DotVVM.Framework.ViewModel.Serialization { public class DotvvmTimeOnlyConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value == null) { @@ -35,10 +35,10 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s } else if (reader.TokenType == JsonToken.Date) { - return (TimeOnly)reader.Value; + return (TimeOnly)reader.Value!; } else if (reader.TokenType == JsonToken.String - && TimeOnly.TryParseExact((string)reader.Value, "O", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) + && TimeOnly.TryParseExact((string)reader.Value!, "O", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) { return date; } diff --git a/src/Framework/Framework/ViewModel/Serialization/ViewModelJsonConverter.cs b/src/Framework/Framework/ViewModel/Serialization/ViewModelJsonConverter.cs index ab21882fd6..65e34b1ceb 100644 --- a/src/Framework/Framework/ViewModel/Serialization/ViewModelJsonConverter.cs +++ b/src/Framework/Framework/ViewModel/Serialization/ViewModelJsonConverter.cs @@ -67,7 +67,7 @@ public override bool CanConvert(Type objectType) /// /// Reads the JSON representation of the object. /// - public override object? ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (existingValue is {}) { @@ -111,8 +111,13 @@ public override bool CanConvert(Type objectType) /// /// Writes the JSON representation of the object. /// - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { + if (value == null) + { + writer.WriteNull(); + return; + } var evSuppressLevel = evWriter.Value.SuppressedLevel; try { diff --git a/src/Framework/Framework/ViewModel/Serialization/ViewModelTypeMetadataSerializer.cs b/src/Framework/Framework/ViewModel/Serialization/ViewModelTypeMetadataSerializer.cs index c70c202aa1..88efa3afcc 100644 --- a/src/Framework/Framework/ViewModel/Serialization/ViewModelTypeMetadataSerializer.cs +++ b/src/Framework/Framework/ViewModel/Serialization/ViewModelTypeMetadataSerializer.cs @@ -217,7 +217,7 @@ private JObject BuildEnumTypeMetadata(Type type) var values = new JObject(); foreach (var v in enumValues) { - values.Add(ReflectionUtils.ToEnumString(type, v.Name), JToken.FromObject(v.Value)); + values.Add(ReflectionUtils.ToEnumString(type, v.Name), v.Value is null ? JValue.CreateNull() : JToken.FromObject(v.Value)); } e["values"] = values; diff --git a/src/Framework/Testing/ControlTestHelper.cs b/src/Framework/Testing/ControlTestHelper.cs index c211aca007..2655c0f84f 100644 --- a/src/Framework/Testing/ControlTestHelper.cs +++ b/src/Framework/Testing/ControlTestHelper.cs @@ -299,7 +299,7 @@ TestDotvvmRequestContext initialContext public ControlTestHelper TestHelper { get; } public string FilePath { get; } public JObject ResultJson { get; } - public JObject ViewModelJson => (JObject)ResultJson["viewModel"]; + public JObject ViewModelJson => (JObject)ResultJson["viewModel"].NotNull(); public dynamic ViewModel => ViewModelJson; public string OutputString { get; } public string? HeadResources { get; } @@ -359,7 +359,7 @@ public async Task RunCommand(string text, Func? if (applyChanges) { JsonUtils.Patch( - (JObject)this.ResultJson["viewModel"], + (JObject)this.ResultJson["viewModel"]!, r.ViewModelJson! ); } diff --git a/src/Samples/Api.Common/DotVVM.Samples.BasicSamples.Api.Common.csproj b/src/Samples/Api.Common/DotVVM.Samples.BasicSamples.Api.Common.csproj index f18ee4d1fc..81dd209ec7 100644 --- a/src/Samples/Api.Common/DotVVM.Samples.BasicSamples.Api.Common.csproj +++ b/src/Samples/Api.Common/DotVVM.Samples.BasicSamples.Api.Common.csproj @@ -6,7 +6,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Samples/Api.Owin/Web.config b/src/Samples/Api.Owin/Web.config index 66d4915c97..f00ebb64ea 100644 --- a/src/Samples/Api.Owin/Web.config +++ b/src/Samples/Api.Owin/Web.config @@ -51,7 +51,7 @@ - + @@ -73,4 +73,4 @@ - \ No newline at end of file + diff --git a/src/Samples/ApplicationInsights.Owin/DotVVM.Samples.ApplicationInsights.Owin.csproj b/src/Samples/ApplicationInsights.Owin/DotVVM.Samples.ApplicationInsights.Owin.csproj index d8d0a6fe71..85d3e57047 100644 --- a/src/Samples/ApplicationInsights.Owin/DotVVM.Samples.ApplicationInsights.Owin.csproj +++ b/src/Samples/ApplicationInsights.Owin/DotVVM.Samples.ApplicationInsights.Owin.csproj @@ -50,7 +50,7 @@ - + diff --git a/src/Samples/AspNetCore/DotVVM.Samples.BasicSamples.AspNetCore.csproj b/src/Samples/AspNetCore/DotVVM.Samples.BasicSamples.AspNetCore.csproj index b6ceea9a2f..3d8e953aba 100644 --- a/src/Samples/AspNetCore/DotVVM.Samples.BasicSamples.AspNetCore.csproj +++ b/src/Samples/AspNetCore/DotVVM.Samples.BasicSamples.AspNetCore.csproj @@ -13,7 +13,7 @@ - + $(DefineConstants);RELEASE diff --git a/src/Samples/AspNetCoreLatest/DotVVM.Samples.BasicSamples.AspNetCoreLatest.csproj b/src/Samples/AspNetCoreLatest/DotVVM.Samples.BasicSamples.AspNetCoreLatest.csproj index 78262d95fb..20546e72c1 100644 --- a/src/Samples/AspNetCoreLatest/DotVVM.Samples.BasicSamples.AspNetCoreLatest.csproj +++ b/src/Samples/AspNetCoreLatest/DotVVM.Samples.BasicSamples.AspNetCoreLatest.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Samples/MiniProfiler.Owin/DotVVM.Samples.MiniProfiler.Owin.csproj b/src/Samples/MiniProfiler.Owin/DotVVM.Samples.MiniProfiler.Owin.csproj index c91a361e7f..e56bedeaa5 100644 --- a/src/Samples/MiniProfiler.Owin/DotVVM.Samples.MiniProfiler.Owin.csproj +++ b/src/Samples/MiniProfiler.Owin/DotVVM.Samples.MiniProfiler.Owin.csproj @@ -41,7 +41,7 @@ - + diff --git a/src/Samples/Owin/DotVVM.Samples.BasicSamples.Owin.csproj b/src/Samples/Owin/DotVVM.Samples.BasicSamples.Owin.csproj index b46e5ac133..1b85ac7325 100644 --- a/src/Samples/Owin/DotVVM.Samples.BasicSamples.Owin.csproj +++ b/src/Samples/Owin/DotVVM.Samples.BasicSamples.Owin.csproj @@ -37,7 +37,7 @@ - + diff --git a/src/Samples/Owin/Web.config b/src/Samples/Owin/Web.config index 0ecf55beeb..b5f4f6e328 100644 --- a/src/Samples/Owin/Web.config +++ b/src/Samples/Owin/Web.config @@ -38,7 +38,7 @@ - + @@ -54,4 +54,4 @@ - \ No newline at end of file + diff --git a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs index 7ebdd3bf3c..ad25db1a8c 100644 --- a/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs +++ b/src/Tests/Runtime/ControlTree/DefaultControlTreeResolver/DefaultControlTreeResolverTests.cs @@ -352,7 +352,7 @@ @viewModel System.Collections.IEnumerable "); var control = root.Content.First(r => r.Metadata.Name == nameof(RequiredResource)); - Assert.AreEqual(0, control.Properties.OfType().Where(a => a.PropertyGroup.Prefixes.Contains("")).Count()); + Assert.AreEqual(0, control.Properties.Keys.OfType().Where(a => a.PropertyGroup.Prefixes.Contains("")).Count()); Assert.IsTrue(((DothtmlElementNode)control.DothtmlNode).Attributes.Any(a => a.HasNodeErrors)); } diff --git a/src/Tools/CommandLine/DotVVM.CommandLine.csproj b/src/Tools/CommandLine/DotVVM.CommandLine.csproj index c2775ba7b8..57a431a262 100644 --- a/src/Tools/CommandLine/DotVVM.CommandLine.csproj +++ b/src/Tools/CommandLine/DotVVM.CommandLine.csproj @@ -3,7 +3,7 @@ DotVVM.CommandLine dotnet-dotvvm - netcoreapp3.1 + net6 dotvvmwizard.snk true Exe