Guten Tag zusammen,
ich benötige ein Object als Byte Array und muss dieses auch wieder von einem Byte Array zu einem Object zurückkonvertieren können. Das klingt erstmal nach keinem Problem, allerdings wird das Programm mit NativeAot kompiliert und dann funktioniert es nicht mehr. Hier mein Codeansatz:
BinarySerializer.cs
using System.Runtime.Serialization.Formatters.Binary;
public class BinarySerializer
{
public static byte[] Serialize(object objectToSerialize)
{
var binaryFormatter = new BinaryFormatter();
var memoryStream = new MemoryStream();
binaryFormatter.Serialize(memoryStream, objectToSerialize);
return memoryStream.ToArray();
}
public static object Deserialize(byte[] bytesToDeserialize)
{
var memoryStream = new MemoryStream();
var binaryFormatter = new BinaryFormatter();
memoryStream.Write(bytesToDeserialize, 0, bytesToDeserialize.Length);
memoryStream.Position = 0;
return binaryFormatter.Deserialize(memoryStream);
}
}
Program.cs
[Serializable]
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
try
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};
var byteArray = BinarySerializer.Serialize(weatherForecast);
var restoredWeatherForecast = (WeatherForecast)BinarySerializer.Deserialize(byteArray);
Console.WriteLine(restoredWeatherForecast.Summary);
Console.WriteLine(restoredWeatherForecast.TemperatureCelsius);
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.ReadKey();
}
}
.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PublishAot>true</PublishAot>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Wenn ich nun die IL exe starte. Ausgabe (wie gewünscht):
Hot
25
Wenn ich dann die native exe starte. Ausgabe:
System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property.
---> System.IO.FileNotFoundException: Cannot load assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. No metadata found for this assembly.
at System.Reflection.Runtime.General.ReflectionCoreCallbacksImplementation.Load(AssemblyName, Boolean) + 0x7d
at System.Runtime.Serialization.Formatters.Binary.Converter..cctor() + 0x2e3
at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xc6
--- End of inner exception stack trace ---
at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x167
at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnGCStaticBase(StaticClassConstructionContext*, Object) + 0xd
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.GetAssemblyId(WriteObjectInfo) + 0x68
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object, BinaryFormatterWriter) + 0x1ed
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream, Object) + 0xef
at BinarySerializer.Serialize(Object) + 0x64
at Program.Main(String[]) + 0x88
Hoffe es gibt da eine Möglichkeit die die gewünschte Funktion mit NativeAot funktional macht. Welchen Klassen am Ende benutzt werden ist ja egal. Hauptsach es läuft. Aber nach Möglichkeit ohne Third Party Libraries. Bin auf eure Antworten gespannt.
Mit freundlichen Grüßen
Ich habe nochmal was anderes ausprobiert. Leider funktioniert es nur wieder ohne NativeAot.
using System.Text;
using System.Text.Json;
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
try
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};
var jsonString = JsonSerializer.Serialize(weatherForecast);
var byteArray = Encoding.UTF8.GetBytes(jsonString);
var restoredJsonString = Encoding.UTF8.GetString(byteArray);
var restoredWeatherForecast = JsonSerializer.Deserialize<WeatherForecast>(restoredJsonString);
Console.WriteLine(restoredWeatherForecast.Summary);
Console.WriteLine(restoredWeatherForecast.TemperatureCelsius);
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.ReadKey();
}
}
Ausgabe IL exe:
Hot
25
Ausgabe native exe:
System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'WeatherForecast'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'WeatherForecast'.
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ThrowNotSupportedException(ReadStack&, Utf8JsonReader&, NotSupportedException) + 0x1fe
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader&, Type, JsonSerializerOptions, ReadStack&, T&) + 0xb2
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader&, Type, JsonSerializerOptions, ReadStack&, T&) + 0x1ec
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader&, JsonSerializerOptions, ReadStack&) + 0x34f
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1, JsonTypeInfo, Nullable`1) + 0x109
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1, JsonTypeInfo) + 0xf5
at System.Text.Json.JsonSerializer.Deserialize[TValue](String, JsonSerializerOptions) + 0x58
at Program.Main(String[]) + 0xe3
Binäre Serialisierung kann kein Native AOT.
Deshalb gibt's ja die EnableUnsafeBinaryFormatterSerialization Property, die ist standardmäßig false 😄
Aber warum überhaupt binär serialisieren?
Das gilt ja auch aus gutem Grund als obsolet.
Besser wäre, Du arbeitest mit JSON (System.Text.Json), dafür gibt's einen Sourcegenerator, mit dem dann gar kein Reflection mehr nötig ist, sodass es auch mit Native AOT funktioniert.
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/source-generation
Ich mein, am Ende ist JSON auch nur binär, bloß mit UTF8 kodiert.
Außerdem ist es leichter zu parsen und auch für Menschen leichter zu lesen.
Und wenn Du unbedingt ein unleserliches Format haben will, verschlüssle es, oder mach ein ZIP Archiv daraus.
Wer [...] kann, ist klar im Vorteil.
lock
Alternative fürasync
/await
: https://github.com/loop8ack/AsyncTicketLock
Und Fehlermeldungen vielleicht lesen. Da steht das Problem - und die Lösung - ja drin.
System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
@Palladin007: Hmmm ja, das entspricht dann meinem zweiten Ansatz bzw. zweiten Beitrag. Leider funktioniert das auch nicht. Siehst du den Fehler, den ich gemacht habe?
@Abt: Also entweder verstehe ich die Fehlermeldung nicht richtig oder das betrifft quasi jede Art von Konstruktor. Ist also nicht umsetzbar.
Also irgendwie habe ich glaube ich gerade ein Brett vorm Kopf. Sollte folgendes nicht eigentlich funktionieren?
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
public class WeatherForecast
{
[JsonConstructorAttribute]
public WeatherForecast(DateTimeOffset Date, int TemperatureCelsius, string? Summary)
{
this.Date = Date;
this.TemperatureCelsius = TemperatureCelsius;
this.Summary = Summary;
}
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
try
{
var weatherForecast = new WeatherForecast(DateTime.Parse("2019-08-01"), 25, "Hot");
var jsonString = JsonSerializer.Serialize(weatherForecast);
var byteArray = Encoding.UTF8.GetBytes(jsonString);
var restoredJsonString = Encoding.UTF8.GetString(byteArray);
var restoredWeatherForecast = JsonSerializer.Deserialize<WeatherForecast>(restoredJsonString);
Console.WriteLine(restoredWeatherForecast.Summary);
Console.WriteLine(restoredWeatherForecast.TemperatureCelsius);
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.ReadKey();
}
}
Bekomme aber immer noch folgende Exception:
System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'WeatherForecast'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'WeatherForecast'.
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ThrowNotSupportedException(ReadStack&, Utf8JsonReader&, NotSupportedException) + 0x1fe
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader&, Type, JsonSerializerOptions, ReadStack&, T&) + 0xb2
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader&, Type, JsonSerializerOptions, ReadStack&, T&) + 0x1ec
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader&, JsonSerializerOptions, ReadStack&) + 0x34f
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1, JsonTypeInfo, Nullable`1) + 0x109
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1, JsonTypeInfo) + 0xf5
at System.Text.Json.JsonSerializer.Deserialize[TValue](String, JsonSerializerOptions) + 0x58
at Program.Main(String[]) + 0xe3
Funktioniert einwandfrei.
public class WeatherForecast
{
public WeatherForecast() { }
public WeatherForecast(DateTimeOffset date, int temperatureCelsius, string? summary)
{
Date = date;
TemperatureCelsius = temperatureCelsius;
Summary = summary;
}
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
WeatherForecast weatherForecast = new(DateTime.Parse("2019-08-01"), 25, "Hot");
string jsonString = JsonSerializer.Serialize(weatherForecast);
Console.WriteLine("Object as Json: " + jsonString);
byte[] byteArray = Encoding.UTF8.GetBytes(jsonString);
string restoredJsonString = Encoding.UTF8.GetString(byteArray);
Console.WriteLine("Restored Json: " + restoredJsonString);
WeatherForecast restoredWeatherForecast = JsonSerializer.Deserialize<WeatherForecast>(restoredJsonString);
Console.WriteLine("From Deserialized " + restoredWeatherForecast.Summary);
}
}
Console Output:
Object as Json: {"Date":"2019-08-01T00:00:00+02:00","TemperatureCelsius":25,"Summary":"Hot"}
Restored Json: {"Date":"2019-08-01T00:00:00+02:00","TemperatureCelsius":25,"Summary":"Hot"}
From Deserialized Hot
C:\source\temp\ConsoleApp13\ConsoleApp13\bin\Debug\net8.0\ConsoleApp13.exe (process 126296) exited with code 0.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ich glaube du hast vergessen in deiner .csproj <PublishAot>true</PublishAot> hinzuzufügen. Bei deinem Quellcode bekomme ich folgenden Output:
Object as Json: {}
Restored Json: {}
Unhandled Exception: System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'WeatherForecast'. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
---> System.NotSupportedException: Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported. Type 'WeatherForecast'.
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ThrowNotSupportedException(ReadStack&, Utf8JsonReader&, NotSupportedException) + 0x33a
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader&, Type, JsonSerializerOptions, ReadStack&, T&) + 0xd3
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader&, Type, JsonSerializerOptions, ReadStack&, T&, Boolean&) + 0x225
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader&, JsonSerializerOptions, ReadStack&) + 0x350
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1, JsonTypeInfo`1, Nullable`1) + 0xfe
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1, JsonTypeInfo`1) + 0x108
at System.Text.Json.JsonSerializer.Deserialize[TValue](String, JsonSerializerOptions) + 0x4c
at Program.Main(String[] args) + 0x11c
at JsonSerializerConsole!<BaseAddress>+0x20ea69
Ne, hab ich nicht. Ist drin.
Wüsste jetzt nicht, was ich sonst für ein Setting vergessen hätte.
Auch die Exe als Release-Exe ohne Debugging/VS funktioniert einwandfrei.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Mein Fehler - das kann so gar nicht klappen. Hab auch gesehen, dass mein Publish nicht korrekt war.
Daher gings bei mir.
Kurz nachgelesen: Du musst Deine Serialisierung als Source Code Generator wählen, ansonsten funktioniert NativeAoT nicht, weil sonst Reflection.
Siehe Docs dazu: Specify source generation mode
Siehe GitHub dazu: [Native-AOT] Using Json Serialize and Deserialize with Native AOT
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Welche .NET Version kommt denn zum Einsatz?
Wenn ich die Ausgabe von @Abt richtig sehe, kommt dort 8.0 zum Einsatz.
@Abt: Funktioniert. Perfekt. Besten Dank!
JsonContext.cs
using System.Text.Json.Serialization;
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}
Program.cs
using System.Text;
using System.Text.Json;
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
try
{
var weatherForecast = new WeatherForecast()
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};
var jsonString = JsonSerializer.Serialize(weatherForecast!, SourceGenerationContext.Default.WeatherForecast);
var byteArray = Encoding.UTF8.GetBytes(jsonString);
var restoredJsonString = Encoding.UTF8.GetString(byteArray);
var restoredWeatherForecast = JsonSerializer.Deserialize<WeatherForecast>(restoredJsonString, SourceGenerationContext.Default.WeatherForecast);
Console.WriteLine(restoredWeatherForecast.Summary);
Console.WriteLine(restoredWeatherForecast.TemperatureCelsius);
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.ReadKey();
}
}
Eine Frage hab ich allerdings noch. Was bewirkt das Ausrufezeichen hinter weatherForecast?
var jsonString = JsonSerializer.Serialize(weatherForecast!, SourceGenerationContext.Default.WeatherForecast);
@david.m: Funktioniert auch mit .NET 7.
Siehe ! (null-forgiving) operator (C# reference)
Hilft Dir qualitativ besseren und stabileren Code mit einer OOP-Sprache zu schreiben, in dem Du Code so schreibst, dass Du es nicht brauchst 😃
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ah okay. Danke.