diff --git a/Katheryne.Tests/Katheryne.Tests.csproj b/Katheryne.Tests/Katheryne.Tests.csproj
new file mode 100644
index 0000000..596f8e3
--- /dev/null
+++ b/Katheryne.Tests/Katheryne.Tests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/Katheryne.Tests/Usings.cs b/Katheryne.Tests/Usings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/Katheryne.Tests/Usings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/Katheryne.Tests/YamlDeserializerTest1.yaml b/Katheryne.Tests/YamlDeserializerTest1.yaml
new file mode 100644
index 0000000..9553c70
--- /dev/null
+++ b/Katheryne.Tests/YamlDeserializerTest1.yaml
@@ -0,0 +1,16 @@
+robotName: 凯瑟琳
+stages:
+ - name: start
+ answer: 向着星辰和深渊!欢迎来到冒险家协会。
+ transformers:
+ - pattern: .*?
+ nextStageName: running
+
+ - name: running
+ answer: 对不起,做不到。
+ transformers:
+ - pattern: .*?
+ nextStageName: running
+beginStageName: start
+
+
\ No newline at end of file
diff --git a/Katheryne.Tests/YamlDeserializerTests.cs b/Katheryne.Tests/YamlDeserializerTests.cs
new file mode 100644
index 0000000..9a90a7d
--- /dev/null
+++ b/Katheryne.Tests/YamlDeserializerTests.cs
@@ -0,0 +1,42 @@
+using Katheryne.Models;
+using Katheryne.Services;
+using YamlDotNet.Serialization;
+
+namespace Katheryne.Tests;
+
+public class YamlDeserializerTests
+{
+ private readonly YamlDeserializerFactory _factory = new();
+
+ [Fact]
+ public void DeserializerTest1()
+ {
+ const string document =
+ """
+ robotName: 凯瑟琳
+ stages:
+ - name: start
+ answer: 向着星辰和深渊!欢迎来到冒险家协会。
+ transformers:
+ - pattern: .*?
+ nextStageName: running
+
+ - name: running
+ answer: 对不起,做不到。
+ transformers:
+ - pattern: .*?
+ nextStageName: running
+ beginStageName: start
+ """;
+
+ IDeserializer deserializer = _factory.GetDeserializer();
+ LexicalModel actual = deserializer.Deserialize(document);
+
+ Assert.Equal("凯瑟琳", actual.RobotName);
+ Assert.Equal("start", actual.BeginStageName);
+
+ Assert.Contains(actual.Stages, s => s.Name == "start");
+ Assert.Contains(actual.Stages, s => s.Name == "running");
+ }
+
+}
\ No newline at end of file
diff --git a/Katheryne.sln b/Katheryne.sln
index 89b4f08..d1e4dcf 100644
--- a/Katheryne.sln
+++ b/Katheryne.sln
@@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Frontend", "Frontend\Fronte
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Katheryne", "Katheryne\Katheryne.csproj", "{74F745FB-2F2B-4B56-A387-3B490EFE9615}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Katheryne.Tests", "Katheryne.Tests\Katheryne.Tests.csproj", "{F8577DDF-85FB-4610-A8AC-38C390BC80AB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -18,5 +20,9 @@ Global
{74F745FB-2F2B-4B56-A387-3B490EFE9615}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74F745FB-2F2B-4B56-A387-3B490EFE9615}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74F745FB-2F2B-4B56-A387-3B490EFE9615}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8577DDF-85FB-4610-A8AC-38C390BC80AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8577DDF-85FB-4610-A8AC-38C390BC80AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8577DDF-85FB-4610-A8AC-38C390BC80AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8577DDF-85FB-4610-A8AC-38C390BC80AB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/Katheryne/Katheryne.csproj b/Katheryne/Katheryne.csproj
index 7a99e3a..4615fc9 100644
--- a/Katheryne/Katheryne.csproj
+++ b/Katheryne/Katheryne.csproj
@@ -7,7 +7,9 @@
+
+
diff --git a/Katheryne/Models/LexicalModel.cs b/Katheryne/Models/LexicalModel.cs
new file mode 100644
index 0000000..5b150c2
--- /dev/null
+++ b/Katheryne/Models/LexicalModel.cs
@@ -0,0 +1,10 @@
+namespace Katheryne.Models;
+
+public class LexicalModel
+{
+ public required string RobotName { get; set; }
+
+ public required List Stages { get; set; }
+
+ public required string BeginStageName { get; set; }
+}
\ No newline at end of file
diff --git a/Katheryne/Models/Stage.cs b/Katheryne/Models/Stage.cs
new file mode 100644
index 0000000..09722f2
--- /dev/null
+++ b/Katheryne/Models/Stage.cs
@@ -0,0 +1,30 @@
+namespace Katheryne.Models;
+
+public class Stage
+{
+ public required string Name { get; set; }
+
+ public required List Transformers { get; set; }
+
+ public required string Answer { get; set; }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is not Stage other)
+ {
+ return false;
+ }
+
+ return other.Name == Name;
+ }
+
+ public override int GetHashCode()
+ {
+ return Name.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+}
\ No newline at end of file
diff --git a/Katheryne/Models/Transformer.cs b/Katheryne/Models/Transformer.cs
new file mode 100644
index 0000000..fcd5d6e
--- /dev/null
+++ b/Katheryne/Models/Transformer.cs
@@ -0,0 +1,8 @@
+namespace Katheryne.Models;
+
+public class Transformer
+{
+ public required string Pattern { get; set; }
+
+ public required string NextStageName { get; set; }
+}
\ No newline at end of file
diff --git a/Katheryne/Services/YamlDeserializerFactory.cs b/Katheryne/Services/YamlDeserializerFactory.cs
new file mode 100644
index 0000000..7f1c8e9
--- /dev/null
+++ b/Katheryne/Services/YamlDeserializerFactory.cs
@@ -0,0 +1,20 @@
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
+
+namespace Katheryne.Services;
+
+public class YamlDeserializerFactory
+{
+ private readonly DeserializerBuilder _builder;
+
+ public YamlDeserializerFactory()
+ {
+ _builder = new DeserializerBuilder();
+ _builder.WithNamingConvention(CamelCaseNamingConvention.Instance);
+ }
+
+ public IDeserializer GetDeserializer()
+ {
+ return _builder.Build();
+ }
+}
\ No newline at end of file