首页 > 解决方案 > 如何将 YAML 属性反序列化为具有传递参数的类?

问题描述

我正在尝试使用 YamlDotNet 实现在游戏“RimWorld”中使用 XML 实现的功能。

我知道如何反序列化基本信息,如下所示:instanceOfDeserializer.Deserialize<MyBaseClass>(someTextReaderInstance)

我正在尝试将派生类型列表反序列化为它们的基本类型列表。

目标是能够将字符串作为类名,并使用 YAML 中列出的参数对其进行实例化。以下是它在 XML 中的实现方式:

<ThingDef ParentName="ResourceBase">
    <defName>Chemfuel</defName>
    <label>chemfuel</label>
    <description>A volatile liquid chemical. Used to fuel engines and rockets, or to transmute into propellant for projectiles, or as an incendiary weapon.</description>
    <stackLimit>150</stackLimit>
    <comps>
      <li Class="CompProperties_Explosive">
        <explosiveRadius>1.1</explosiveRadius>
        <explosiveDamageType>Flame</explosiveDamageType>
        <explosiveExpandPerStackcount>0.037</explosiveExpandPerStackcount>
        <startWickOnDamageTaken>
          <li>Flame</li>
        </startWickOnDamageTaken>
        <startWickHitPointsPercent>0.333</startWickHitPointsPercent>
        <preExplosionSpawnThingDef>Filth_Fuel</preExplosionSpawnThingDef>
        <preExplosionSpawnChance>1</preExplosionSpawnChance>
        <wickTicks>70~150</wickTicks>
      </li>
    </comps>
  </ThingDef>

comps列表是我想参考的外部类的列表。在这种情况下,我们想为 item 添加爆炸属性chemfuel。似乎在 XML 中,开发人员使用Class="CompProperties_Explosive"标签引用了想要的类。我需要在 YAML 文档中使用类似的功能。

以下是我在 YAML 文档中的设想:

Type: Item
Name: fuel_cansiter
DisplayName: Fuel Canister
Sprite: fuel_canister_1
MaxStackSize: 10
MaxHP: 15
Value: 30
Components:
      - Explosive:
        - Damage: 10
        - Range: 25
      - Burnable:
        - FlameSize: 10
        - HealthThreshold: 0.4

我想用和类作为组件来实例化这个FuelCanister项目,但是通过 YAML 传递动态变量,例如损坏和范围。Explosive.csBurnable.cs

在这种情况下,派生类型是Explosiveand Burnable,它们都应该能够反序列化为它们的基本类型,例如Component

最后,FuelCanister.cs该类应该以一个Component类型列表结束,从中派生 和ExplosiveBurnable

YamlDotNet 中是否有内置函数可以轻松执行此操作?

编辑:也许 XML 方式使用 XML 属性来提取类的名称,然后使用该名称,我们可以使用反射来实例化具有传递参数的类?有没有办法用 YAML 做到这一点?

标签: c#yamldotnet

解决方案


在 YAML 中,您通常使用tags指定类型。使用标签,您的 YAML 可能如下所示:

!Item
Name: fuel_cansiter
DisplayName: Fuel Canister
Sprite: fuel_canister_1
MaxStackSize: 10
MaxHP: 15
Value: 30
Components:
      - !Explosive
        Damage: 10
        Range: 25
      - !Burnable
        FlameSize: 10
        HealthThreshold: 0.4

在本文档中!Item!Explosive!Burnable是标签。您可以定义它们映射到的类型。使用DeserializerBuilder该类,您可以将标签与 .NET 类型相关联,如下所示:

var deserializer = new DeserializerBuilder()
    .WithTagMapping("!Item", typeof(Item))
    .WithTagMapping("!Explosive", typeof(Explosive))
    .WithTagMapping("!Burnable", typeof(Burnable))
    .Build();

这假设您有以下类:

class Item
{
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public string Sprite { get; set; }
    public int MaxStackSize { get; set; }
    public int MaxHP { get; set; }
    public int Value { get; set; }
    public List<Component> Components { get; set; }
}

abstract class Component {}

class Explosive : Component
{
    public int Damage { get; set; }
    public int Range { get; set; }
}

class Burnable : Component
{
    public int FlameSize { get; set; }
    public double HealthThreshold { get; set; }
}

一旦你有了这个,你可以像这样反序列化:

var item = deserializer.Deserialize<object>(yaml);

在这种情况下,我使用object了因为我假设您可能希望您的文档包含不同的对象类型。如果您事先知道该文档包含一个Item,则可以删除该!Item标签并使用它:

var item = deserializer.Deserialize<Item>(yaml);

您可以在 YamlDotNet 的 wiki中找到有关该方法的一些文档WithTagMapping


推荐阅读