diff --git a/I18N Commander/DataModel/Database/Common/DataContext.cs b/I18N Commander/DataModel/Database/Common/DataContext.cs index c277cb1..98794ce 100644 --- a/I18N Commander/DataModel/Database/Common/DataContext.cs +++ b/I18N Commander/DataModel/Database/Common/DataContext.cs @@ -1,4 +1,6 @@ -using Microsoft.EntityFrameworkCore; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; namespace DataModel.Database.Common; @@ -63,4 +65,97 @@ public sealed class DataContext : DbContext, IDataContext #endregion } + + #region Export and import data structures + + private readonly record struct JsonData( + IEnumerable Settings, + IEnumerable Sections, + IEnumerable TextElements, + IEnumerable Translations + ); + + internal readonly record struct JsonUniqueId(string Code, Guid UniqueId, string Prefix = "") + { + public override string ToString() => string.IsNullOrWhiteSpace(this.Prefix) ? $"{this.Code}::{this.UniqueId}" : $"{this.Prefix}::{this.Code}::{this.UniqueId}"; + + public static implicit operator string(JsonUniqueId id) => id.ToString(); + } + + private sealed class JsonUniqueIdConverter : JsonConverter + { + public override JsonUniqueId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var json = reader.GetString(); + var parts = json?.Split("::"); + return parts?.Length switch + { + 2 => new JsonUniqueId(parts[0], Guid.Parse(parts[1])), + 3 => new JsonUniqueId(parts[1], Guid.Parse(parts[2]), parts[0]), + + _ => throw new JsonException($"Invalid format of JsonUniqueId: {json}") + }; + } + + public override void Write(Utf8JsonWriter writer, JsonUniqueId value, JsonSerializerOptions options) + { + writer.WriteStringValue(value); + } + } + + internal readonly record struct JsonSetting( + JsonUniqueId UniqueId, + string Code, + string TextValue, + int IntegerValue, + bool BoolValue, + Guid GuidValue + ); + + internal readonly record struct JsonSection( + JsonUniqueId UniqueId, + string Name, + string DataKey, + int Depth, + JsonUniqueId ParentUniqueId, + List TextElements + ); + + internal readonly record struct JsonTextElement( + JsonUniqueId UniqueId, + string Code, + string Name, + bool IsMultiLine, + JsonUniqueId SectionUniqueId, + List Translations + ); + + internal readonly record struct JsonTranslation( + JsonUniqueId UniqueId, + string Culture, + string Text, + bool TranslateManual, + JsonUniqueId TextElementUniqueId + ); + + #endregion + + public async Task ExportAsync(string path) + { + var jsonSettings = new JsonSerializerOptions + { + WriteIndented = true, + Converters = { new JsonUniqueIdConverter() }, + }; + + await using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); + await JsonSerializer.SerializeAsync(fileStream, + new JsonData + { + Settings = this.Settings.Select(n => n.ToJsonSetting()), + Sections = this.Sections.Select(n => n.ToJsonSection()), + TextElements = this.TextElements.Select(n => n.ToJsonTextElement()), + Translations = this.Translations.Select(n => n.ToJsonTranslation()), + }, jsonSettings); + } } \ No newline at end of file diff --git a/I18N Commander/DataModel/Database/Common/IDataContext.cs b/I18N Commander/DataModel/Database/Common/IDataContext.cs index a590766..7888beb 100644 --- a/I18N Commander/DataModel/Database/Common/IDataContext.cs +++ b/I18N Commander/DataModel/Database/Common/IDataContext.cs @@ -11,4 +11,6 @@ public interface IDataContext public DbSet TextElements { get; set; } public DbSet Translations { get; set; } + + public Task ExportAsync(string path); } \ No newline at end of file diff --git a/I18N Commander/DataModel/Database/Section.cs b/I18N Commander/DataModel/Database/Section.cs index bb3a2c1..23cbf8f 100644 --- a/I18N Commander/DataModel/Database/Section.cs +++ b/I18N Commander/DataModel/Database/Section.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using DataModel.Database.Common; namespace DataModel.Database; @@ -18,4 +19,16 @@ public sealed class Section public Section? Parent { get; set; } public List TextElements { get; set; } = new(); + + internal DataContext.JsonUniqueId JsonUniqueId => new(this.DataKey, this.UniqueId, "Sec"); + + internal DataContext.JsonSection ToJsonSection() => new() + { + UniqueId = this.JsonUniqueId, + Name = this.Name, + DataKey = this.DataKey, + Depth = this.Depth, + ParentUniqueId = this.Parent?.JsonUniqueId ?? new DataContext.JsonUniqueId("null", Guid.Empty, "Sec"), + TextElements = this.TextElements.Select(n => n.JsonUniqueId).ToList() + }; } \ No newline at end of file diff --git a/I18N Commander/DataModel/Database/Setting.cs b/I18N Commander/DataModel/Database/Setting.cs index 8d0303a..4c5f1fc 100644 --- a/I18N Commander/DataModel/Database/Setting.cs +++ b/I18N Commander/DataModel/Database/Setting.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using DataModel.Database.Common; namespace DataModel.Database; @@ -18,4 +19,16 @@ public sealed class Setting public int IntegerValue { get; set; } public Guid GuidValue { get; set; } + + internal DataContext.JsonUniqueId JsonUniqueId => new(this.Code, this.UniqueId, "Set"); + + internal DataContext.JsonSetting ToJsonSetting() => new() + { + UniqueId = this.JsonUniqueId, + Code = this.Code, + BoolValue = this.BoolValue, + GuidValue = this.GuidValue, + IntegerValue = this.IntegerValue, + TextValue = this.TextValue, + }; } \ No newline at end of file diff --git a/I18N Commander/DataModel/Database/TextElement.cs b/I18N Commander/DataModel/Database/TextElement.cs index e1c2154..adf3f86 100644 --- a/I18N Commander/DataModel/Database/TextElement.cs +++ b/I18N Commander/DataModel/Database/TextElement.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using DataModel.Database.Common; namespace DataModel.Database; @@ -18,4 +19,16 @@ public sealed class TextElement public Section Section { get; set; } public List Translations { get; set; } = new(); + + internal DataContext.JsonUniqueId JsonUniqueId => new(this.Code, this.UniqueId, "TXT"); + + internal DataContext.JsonTextElement ToJsonTextElement() => new() + { + UniqueId = this.JsonUniqueId, + Code = this.Code, + Name = this.Name, + IsMultiLine = this.IsMultiLine, + SectionUniqueId = this.Section.JsonUniqueId, + Translations = this.Translations.Select(n => n.JsonUniqueId).ToList(), + }; } \ No newline at end of file diff --git a/I18N Commander/DataModel/Database/Translation.cs b/I18N Commander/DataModel/Database/Translation.cs index b654f73..167d671 100644 --- a/I18N Commander/DataModel/Database/Translation.cs +++ b/I18N Commander/DataModel/Database/Translation.cs @@ -1,4 +1,6 @@ -namespace DataModel.Database; +using DataModel.Database.Common; + +namespace DataModel.Database; public sealed class Translation { @@ -13,4 +15,15 @@ public sealed class Translation public string Text { get; set; } = string.Empty; public bool TranslateManual { get; set; } = false; + + internal DataContext.JsonUniqueId JsonUniqueId => new($"{this.TextElement.Code}::{this.Culture}", this.UniqueId, "Trans"); + + internal DataContext.JsonTranslation ToJsonTranslation() => new() + { + UniqueId = this.JsonUniqueId, + Culture = this.Culture, + TranslateManual = this.TranslateManual, + TextElementUniqueId = this.TextElement.JsonUniqueId, + Text = this.Text, + }; } \ No newline at end of file