System.Yaml.Serialization.YamlSerizlizer

English version is here.

このクラスの2つのインスタンスメソッド Serialize Deserialize を使うことで、C# の様々なオブジェクトを、何の準備も無しに YAML に変換することができます。

どんなオブジェクトが変換できますか?

C# のプリミティブ型 (bool, char, int,...), 列挙型(enum), 組み込みの非プリミティブ型(string, decimal), 構造体型(struct), クラス型(class), それらの配列型(xxx[]) を変換できます。

一方で、IntPtr (プリミティブ型ではあるのですが・・・) とポインタ型 (void*, int*, ...) は扱えません。

ということでほぼ何でもOKなのですが、デフォルトコンストラクタ(引数を持たないコンストラクタ)が定義されていないクラスを扱うには、YamlConfig.AddActivator<T>(Func<object>) を使ってインスタンスの生成の方法をライブラリに教える必要があります。

// いろいろごちゃ混ぜの object 配列を用意して
object obj = new object[]{ 
    null,
    "abc", 
    true, 
    1, 
    (Byte)1,
    1.0, 
    "1",
    new double[]{ 1.1, 2, -3 },
    new string[]{ "def", "ghi", "1" },
    new System.Drawing.Point(1,3), 
    new System.Drawing.SolidBrush(Color.Blue)
};

// いきなりシリアライズできてしまいます
var serializer = new YamlSerializer();
string yaml = serializer.Serialize(obj);
// %YAML 1.2
// ---
// - null
// - abc
// - True
// - 1
// - !System.Byte 1
// - !!float 1
// - "1"
// - !<!System.Double[]> [1.1, 2, -3]
// - !<!System.String[]>
//   - def
//   - ghi
//   - "1"
// - !System.Drawing.Point 1, 3
// - !System.Drawing.SolidBrush
//   Color: Blue
// ...

// SolidBrush はデフォルトコンストラクタを持たないので
// デシリアライズがうまく行きません
object restored;
try {
    restored = YamlSerializer.Deserialize(yaml)[0];
} catch(MissingMethodException) {
    // default constructor is missing for SolidBrush
}

// SolidBrush の作り方をライブラリに教えてやります。
YamlNode.DefaultConfig.AddActivator<System.Drawing.SolidBrush>(
    () => new System.Drawing.SolidBrush(Color.Black /* dummy */));

// 今度はうまく行きます。
restored = serializer.Deserialize(yaml)[0];

YamlSerializer が書き出す YAML ドキュメントは YAML ディレクティブ(%YAML 1.2) と、明示的なドキュメントの開始・終了マーク("---" および "...")を持っています。これにより、複数のオブジェクトを連続して1つのYAMLストリームに書き出すことが可能になります。

var yaml = "";
var serializer = new YamlSerializer();
yaml += serializer.Serialize("a");
yaml += serializer.Serialize(1);
yaml += serializer.Serialize(1.1);
// %YAML 1.2
// ---
// a
// ...
// %YAML 1.2
// ---
// 1
// ...
// %YAML 1.2
// ---
// 1.1
// ...

object[] objects = serializer.Deserialize(yaml);
// objects[0] == "a"
// objects[1] == 1
// objects[2] == 1.1

このように、1つの YAML ストリームに複数のオブジェクトを格納することができるため、Deserialize(...) メソッドは object の配列を返すようになっています。

.NET の型と YAML のタグとの対応関係

C# Type .NET Type YAML Type/Tag
bool System.Boolean !!bool
int System.Int32 !!int
double System.Double !!float
string System.String !!str, !!null
N/A N/A !!binary
DateTime System.DateTime !!timestamp
object[] System.Object[] !!seq
Dictionary<object,object> ... !!map


Dictionary<object,object> は正式には System.Collections.Generic.Dictionary`2[System.Object,System.Object] と .NET では呼ばれます。

YAML の !!binary に対応する型がありませんが、その代わりに YamlSerializeAttribute を使うことで任意の値型配列を base64 形式でシリアライズすることができます。 シリアライゼーションの方法 を参照して下さい。

上の表にない .NET 型は、YAML ではローカルタグで表されます。YAML で言うローカルタグとは "!" から始まる任意形式のタグのことです。

C# Type .NET Type YAML Type/Tag
byte System.Byte !System.Byte
ulong System.UInt64 !System.UInt64
struct MyNamespace.A MyNamespace.A !MyNamespace.A, !!null
class MyNameSpace.A { class B{} } MyNamespace.A+B !MyNamespace.A+B, !!null


すべての .NET の参照型は、 !!null にも対応関係を持つことに注意して下さい。これは YAML では null 値が独立した型 !!null を持っているためです。

クラス型(class)や構造体型(struct)のシリアライゼーション

クラス型や構造体型のシリアライゼーションでは、デフォルトではすべてのパブリックなプロパティおよびフィールドがシリアライズされます。プロテクティッド、プライベート、インターナルなメンバーは対象外です。

シリアライゼーションの方法

値型の読み出し専用プロパティもシリアライゼーションの対象外になります。なぜなら、そのようなプロパティに値を代入する方法がないからです。

一方で、クラス型の読み出し専用プロパティはシリアライゼーションの対象になります。そのようなプロパティのデシリアライズ時には、新しいクラスインスタンスを作ってプロパティに代入する代わりに(それはできないので)、プロパティから読み出したインスタンスのプロパティやフィールドに値を代入する形が取られます。このようなシリアライズの方法をこのライブラリでは YamlSerializeMethod.Content と読んでいます。

書き込み可能なプロパティやフィールドのデシリアライズは、YAML の記載内容を使って新しいオブジェクトが作られた後、そのオブジェクトがそのようなプロパティやフィールドに代入される形で行われます。このようなシリアライズの方法を、このライブラリでは YamlSerializeMethod.Assign と読んでいます。

書き込み可能なプロパティやフィールドに対して明示的に YamlSerializeMethod.Content を指定することもできます。そのようなプロパティの定義時に YamlSerializeAttribute を付けて下さい。

シリアライゼーション方法にはもう1つ合って、それは YamlSerializeMethod.Binary と呼ばれます。これは値型の配列のみに適用可能なもので、配列の個々のメンバーをシリアライズする代わりに、配列をメモリブロックとして直接 YAML テキストに変換することができます。非常に大きな配列を YAML 化する際に役立てて下さい。

YamlSerializeAttribute として YamlSerializeMethod.Never が与えられると、そのメンバーはシリアライズされません。

public class Test1
{
    public int PublicProp { get; set; }         // Assign モードで処理されます
    protected int ProtectedProp { get; set; }           // 無視されます
    private int PrivateProp { get; set; }               // 無視されます
    internal int InternalProp { get; set; }             // 無視されます

    public int PublicField;                     // Assign モードで処理されます
    protected int ProtectedField;                       // 無視されます
    private int PrivateField;                           // 無視されます
    internal int InternalField;                         // 無視されます

    public List<string> ClassPropByAssign       // Assign モードで処理されます
    { get; set; }

    public int ReadOnlyValueProp { get; private set; }  // 無視されます
    public List<string> ReadOnlyClassProp       // Content モードで処理されます
    { get; private set; }

    [YamlSerialize(YamlSerializeMethod.Content)]
    public List<string> ClassPropByContent      // Content モードで処理されます
    { get; set; }

    public int[] IntArrayField =                // Assign モードで処理されます
       new int[10];

    [YamlSerialize(YamlSerializeMethod.Binary)]
    public int[] IntArrayFieldBinary =          // Binary モードで処理されます
       new int[100];

    [YamlSerialize(YamlSerializeMethod.Never)]
    public int PublicPropHidden;                        // 無視されます

    public Test1()
    {
        ClassPropByAssign = new List<string>();
        ReadOnlyClassProp = new List<string>();
        ClassPropByContent = new List<string>();
    }
}

public void TestPropertiesAndFields1()
{
   var test1 = new Test1();
   test1.ClassPropByAssign.Add("abc");
   test1.ReadOnlyClassProp.Add("def");
   test1.ClassPropByContent.Add("ghi");
   var rand = new Random(0);
   for ( int i = 0; i < test1.IntArrayFieldBinary.Length; i++ )
       test1.IntArrayFieldBinary[i] = rand.Next();

   var serializer = new YamlSerializer();
   string yaml = serializer.Serialize(test1);
   // %YAML 1.2
   // ---
   // !YamlSerializerTest.Test1
   // PublicProp: 0
   // ClassPropByAssign: 
   //   Capacity: 4
   //   ICollection.Items: 
   //     - abc
   // ReadOnlyClassProp: 
   //   Capacity: 4
   //   ICollection.Items: 
   //     - def
   // ClassPropByContent: 
   //   Capacity: 4
   //   ICollection.Items: 
   //     - ghi
   // PublicField: 0
   // IntArrayField: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
   // IntArrayFieldBinary: |+2
   //   Gor1XAwenmhGkU5ib9NxR11LXxp1iYlH5LH4c9hImTitWSB9Z78II2UvXSXV99A79fj6UBn3GDzbIbd9
   //   yBDjAyslYm58iGd/NN+tVjuLRCg3cJBo+PWMbIWm9n4AEC0E7LKXWV5HXUNk7I13APEDWFMM/kWTz2EK
   //   s7LzFw2gBjpKugkmQJqIfinpQ1J1yqhhz/XjA3TBxDBsEuwrD+SNevQSqEC+/KRbwgE6D011ACMeyRt0
   //   BOG6ZesRKCtL0YU6tSnLEpgKVBz+R300qD3/W0aZVk+1vHU+auzyGCGUaHCGd6dpRoEhXoIg2m3+AwJX
   //   EJ37T+TA9BuEPJtyGoq+crQMFQtXj1Zriz3HFbReclLvDdVpZlcOHPga/3+3Y509EHZ7UyT7H1xGeJxn
   //   eXPrDDb0Ul04MfZb4UYREOfR3HNzNTUYGRsIPUvHOEW7AaoplIfkVQp19DvGBrBqlP2TZ9atlWUHVdth
   //   7lIBeIh0wiXxoOpCbQ7qVP9GkioQUrMkOcAJaad3exyZaOsXxznFCA==
   // ...
}

フィールドやプロパティのデフォルト値

YamlSerializerSystem.ComponentModel.DefaultValueAttribute を正しく解釈します。したがって、構造体やクラスのメンバーがデフォルト値に等しい値を持っていた場合、そのメンバーは YAML テキストに書き出されません。

YamlSerializerShouldSerializeXXX と言う名前のインスタンスメソッドも参照します。例えば、何らかのクラスの Font というプロパティをシリアライズする直前に、ライブラリは bool ShouldSerializeFont() というメソッドがあるかどうか調べ、見つかればそれを呼び出します。もしこのメソッドが false を返せば Font プロパティは書き出されません。ShouldSerializeXXX メソッドはパブリックである必要はありません。

using System.ComponentModel;

public class Test2
{
    [DefaultValue(0)]
    public int Default0 = 0;

    [DefaultValue("a")]
    public string Defaulta = "a";

    public int DynamicDefault = 0;

    bool ShouldSerializeDynamicDefault()
    {
        return Default0 != DynamicDefault;
    }
}

public void TestDefaultValue()
{
    var test2 = new Test2();
    var serializer = new YamlSerializer();

    // すべてのプロパティはデフォルト値なので書き出されません
    var yaml = serializer.Serialize(test2);
    // %YAML 1.2
    // ---
    // !YamlSerializerTest.Test2 {}
    // ...

    // YAML の記法で {} は空の Mapping ノードを表します


    // デフォルト値と異なれば書き出されます
    test2.Defaulta = "b";
    yaml = serializer.Serialize(test2);
    // %YAML 1.2
    // ---
    // !YamlSerializerTest.Test2
    // Defaulta: b
    // ...

    // 元に戻します
    test2.Defaulta = "a";
    var yaml = serializer.Serialize(test2);
    // %YAML 1.2
    // ---
    // !YamlSerializerTest.Test2 {}
    // ...

    // これで ShouldSerializeDynamicDefault が true を返すようになります
    test2.DynamicDefault = 1;
    yaml = serializer.Serialize(test2);
    // %YAML 1.2
    // ---
    // !YamlSerializerTest.Test2
    // DynamicDefault: 1
    // ...

    // 再び ShouldSerializeDynamicDefault が false を返すようになりますが、
    // 今度は Default0 がデフォルト値ではなくなります
    test2.Default0 = 1;
    yaml = serializer.Serialize(test2);
    // %YAML 1.2
    // ---
    // !YamlSerializerTest.Test2
    // Default0: 1
    // ...
}

TypeConverter

シリアライズ対象の objectTypeConverterAttribute を持っている場合、そのオブジェクトはタイプコンバータを使って文字列としてシリアライズされます。

したがって、System.Drawing.Poing クラスのシリアライズ結果は !System.Drawing.Poing { x: 1, y: 2 } のようではなく、!System.Drawing.Poing 1,2 のようになります。

カルチャー

YamlSerializer は常に System.Globalization.CultureInfo.InvariantCulture を使ってネイティブな .NET オブジェクトを文字列に変換します。したがって、System.Globalization.CultureInfo.CurrentCulture が浮動小数点数を 1.234 ではなく 1,234 のように表す場合にも、 YamlSerializer は正しく YAML の基準に則って 1.234 の形式で値を読み書きします。

コレクションクラス

シリアライズ対象の object がコレクションクラスである場合、すなわち ICollection<T>, IList, IDictionary のいずれかを実装する場合、コレクションクラス自体のパブリックメンバーに加えて、そのコレクションに含まれるオブジェクトもシリアライズされます。その場合には、ICollection.Items または IDictionary.Entries という名前の疑似プロパティが現れて、子オブジェクトがその中に表されることになります。

この例としては、次項のコードを参照して下さい。

1つのオブジェクトが複数回現れる場合の処理

YamlSerializer は C# オブジェクトのグラフ構造を保存します。つまり、ある1つのオブジェクトが、オブジェクトツリーの中の複数箇所から参照されている場合、そのようなオブジェクトは1回だけシリアライズされ、他の位置からの参照は、その1つのオブジェクトへのエイリアスとして表されます。したがって、YAML テキストをデシリアライズした場合にも、オブジェクトの参照構造が正しく保持されることになります。

また、YamlSerializer は直接的に、あるいは間接的に自身を参照するようなオブジェクトを安全に取り扱うことができます。そのようなオブジェクトを単純な再帰処理によってシリアライズしようと試みれば、容易に無限ループに陥ることに注意して下さい。

public class TestClass
{
    public List<TestClass> list = 
        new List<TestClass>();
}

public class ChildClass: TestClass
{
}

void RecursiveObjectsTest()
{
    var a = new TestClass();
    var b = new ChildClass();
    a.list.Add(a); // 間接的に自身を参照するオブジェクトを作成しています
    a.list.Add(a); // 同じオブジェクトが何度も現れています
    a.list.Add(b);
    a.list.Add(a);
    a.list.Add(b);
    b.list.Add(a);

    // シリアライズすると、複数回現れるオブジェクトがアンカーとエイリアスを
    // 使って正しく表されていることが分かります
    var serializer = new YamlSerializer();
    string yaml = serializer.Serialize(a);
    // %YAML 1.2
    // ---
    // &A !TestClass
    // list: 
    //   Capacity: 8
    //   ICollection.Items: 
    //     - *A
    //     - *A
    //     - &B !ChildClass
    //       list: 
    //         Capacity: 4
    //         ICollection.Items: 
    //           - *A
    //     - *A
    //     - *B
    // ...

    // デシリアライズします
    var restored = (TestClass)serializer.Deserialize(yaml)[0];

    // オブジェクト構造が保たれていることを確認します
    Assert.IsTrue(restored == restored.list[0]);
    Assert.IsTrue(restored == restored.list[1]);
    Assert.IsTrue(restored == restored.list[3]);
    Assert.IsTrue(restored == restored.list[5]);
    Assert.IsTrue(restored.list[2] == restored.list[4]);
}

同じクラス型でも、string 型は例外として扱われます。1つの string オブジェクトが複数回現れる構造を YAML 化した場合には、同じ文字列値が何回も書き出されることになります。ただしこれにも例外があって、文字列がとても長い場合(1000文字以上の時)には、エイリアスが使われます。

// 1000 文字の長い文字列
string long_str =
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
    "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
string short_str = "12345"; // 通常の文字列

// 複数回現れる構造
object obj = new object[] { long_str, long_str, short_str, short_str };

// 長い文字列はエイリアスを使って、短い文字列は値として、書き込まれます
var serializer = new YamlSerializer();
string yaml = serializer.Serialize(obj);
// %YAML 1.2
// ---
// - &A 01234567890123456789012345678901234567890123456789 ... (snip) ... 789
// - *A
// - "12345"
// - "12345"
// ...

YamlSerializer が扱う YAML 文書

シリアライズ時、YamlSerializer は YAML 1.2 で定義される様々なスタイル、すなわちブロックスタイル、フロースタイル、明示的・非明示的なマッピングなどを効率的に使って、なるべく読みやすい YAML テキストを出力しようとします。

[Flags]
enum TestEnum: uint 
{ 
    abc = 1, 
    あいう = 2 
} 

public void TestVariousFormats()
{
    var dict = new Dictionary<object, object>();
    dict.Add(new object[] { 1, "a" }, new object());
    object obj = new object[]{
        dict,
        null,
        "abc",
        "1",
        "a ",
        "- a",
        "abc\n", 
        "abc\ndef\n", 
        "abc\ndef\nghi", 
        new double[]{ 1.1, 2, -3, 3.12, 13.2 },
        new int[,] { { 1, 3}, {4, 5}, {10, 1} },
        new string[]{ "jkl", "mno\npqr" },
        new System.Drawing.Point(1,3),
        TestEnum.abc,
        TestEnum.abc | TestEnum.あいう,
    };
    var config = new YamlConfig();
    config.ExplicitlyPreserveLineBreaks = false;
    var serializer = new YamlSerializer(config);
    string yaml = serializer.Serialize(obj);

    // %YAML 1.2
    // ---
    // - !<!System.Collections.Generic.Dictionary%602[[System.Object,...],[System.Object,...]]>
    //   IDictionary.Entries: 
    //     ? - 1
    //       - a
    //     : !System.Object {}
    // - null
    // - abc
    // - "1"
    // - "a "
    // - "- a"
    // - "abc\n"
    // - |+2
    //   abc
    //   def
    // - |-2
    //   abc
    //   def
    //     ghi
    // - !<!System.Double[]> [1.1, 2, -3, 3.12, 13.2]
    // - !<!System.Int32[,]> [[1, 3], [4, 5], [10, 1]]
    // - !<!System.String[]>
    //   - jkl
    //   - |-2
    //     mno
    //     pqr
    // - !System.Drawing.Point 1, 3
    // - !TestEnum abc
    // - !TestEnum abc, あいう
    // ...
}

デシリアライズ時には YamlSerializer は任意のスタイルで記述された YAML 1.2 ドキュメントを受け入れることができます。TAG ディレクティブ、コメント、ブロックスタイル・フロースタイル、明示的・非明示的マッピングなどを自由に用いて C# オブジェクトを記述できます。

YAML 中の改行文字

デフォルトで、YamlSerializer の出力する YAML テキストの改行文字は "\r\n" になります。異なる改行文字を使いたければ、YamlNode.DefaultConfig.LineBreakForOutput を変更するか、YamlConfig のインスタンスを作って YamlSerializer のコンストラクタに渡して下さい。

// デフォルトでは \r\n
var serializer = new YamlSerializer();
var yaml = serializer.Serialize("abc");
// %YAML 1.2\r\n    # この例では改行文字を明示してあります
// ---\r\n
// abc\r\n
// ...\r\n

// \n にするようカスタマイズした YamlSerializer を作ります
var config = new YamlConfig();
config.LineBreakForOutput = "\n";
serializer = new YamlSerializer(config);
var yaml = serializer.Serialize("abc");
// %YAML 1.2\n
// ---\n
// abc\n
// ...\n

// デフォルトの動作を変更します
YamlNode.DefaultConfig.LineBreakForOutput = "\n";

var serializer = new YamlSerializer();
serializer = new YamlSerializer();
var yaml = serializer.Serialize("abc");
// %YAML 1.2\n
// ---\n
// abc\n
// ...\n

YAML に出力される文字列値が複数行にわたる場合、デフォルトではそのような改行文字はエスケープシーケンスを使って明示的に YAML テキスト中に表されます。これによって、生成される YAML がとても読みにくくなってしまうのですが、文字列値を正確に保存するためにはこうするしかないというのが現状です。というのも、YAML 1.2 は YAML を処理するパーサーは、エスケープされていない改行文字をすべて "\n" に変換しなければならないと取り決めていますので、Windows 環境の \r\n で区切られた文字列値は、YAML へのシリアライズ・デシリアライズで値が変化してしまうことになるのです。

以下でこの問題の解決方法が説明されるのですが、とりあえず、出力される YAML テキストを読みやすく保つには、YamlConfig.ExplicitlyPreserveLineBreaksfalse を代入して下さい。これにより、複数行にわたる文字列はリテラルスタイルで書き出されることになります。

もちろん、このままではデシリアライズ時にすべての改行文字列は "\n" になってしまいます。

// \r\n で区切られた文字列値
var text = "abc\r\n  def\r\nghi\r\n";
// abc
//   def
// ghi

var serializer = new YamlSerializer();

// デフォルトでは、すべての改行文字は \ でエスケープされて
// YAML ソース中に明示的に現れます
// この例では本当の改行文字は表示していないことに注意して下さい
var yaml = serializer.Serialize(text);
// %YAML 1.2
// ---
// "abc\r\n\
// \  def\r\n\
// ghi\r\n"
// ...

// これをデシリアライズすれば本来の値が保持されています
var restored = (string)serializer.Deserialize(yaml)[0];
// "abc\r\n  def\r\nghi\r\n"

// 設定を変更します
YamlNode.DefaultConfig.ExplicitlyPreserveLineBreaks = false;

// リテラルスタイルで出力された文字列はずっと読みやすい
// ものになります
var yaml = serializer.Serialize(text);
// %YAML 1.2
// ---
// |+2
//   abc
//     def
//   ghi
// ...

// しかし、改行文字はすべて \n になってしまいます
var restored = (string)serializer.Deserialize(yaml)[0];
// "abc\n  def\nghi\n"

この問題に対して、本ライブラリは2つの回避策を提示します。が、それらはどちらを使った場合にも YAML の規定する YAML パーサーの処理とは異なる物になりますので、生成された YAML テキストの取り扱いには注意して下さい。

回避策の1つ目は、YamlConfig.LineBreakForInput "\r\n" にすることです。これにより、YAML パーサーはすべての改行文字を "\n" の代わりに "\r\n" に変換します。

もう1つは、YamlConfig.NormalizeLineBreaks false にすることです。これにより改行文字の規格化は全く行われなくなり、複数行文字列値の中の改行文字はそのままの形で書き出し、読み込みされることになります。

var serializer = new YamlSerializer();

// さまざまな改行文字を含む文字列値
var text = "abc\r  def\nghi\r\n"; 
// abc\r        # この例では改行文字が明示されています
//   def\n
// ghi\r\n

// 改行文字をエスケープしないようにします
YamlNode.DefaultConfig.ExplicitlyPreserveLineBreaks = false;

// シリアライズ時、すべての改行文字は 
// "\r\n" (= YamlNode.DefaultConfig.LineBreakForOutput) に規格化されます
var yaml = serializer.Serialize(text);
// %YAML 1.2\r\n
// ---\r\n
// |+2\r\n
//   abc\r\n
//     def\r\n
//   ghi\r\n
// ...\r\n

// デシリアライズ時、すべての改行文字は 
// "\n" (= YamlNode.DefaultConfig.LineBreakForInput) に規格化されます
var restored = (string)serializer.Deserialize(yaml)[0];
// "abc\n  def\nghi\n"

// デシリアライズ時に "\n" ではなく "\r\n" に規格化するようにします
YamlNode.DefaultConfig.LineBreakForInput = "\r\n";
restored = (string)serializer.Deserialize(yaml)[0];
// "abc\r\n  def\r\nghi\r\n"

// 改行文字の規格化をしないようにします
YamlNode.DefaultConfig.NormalizeLineBreaks = false;
var yaml = serializer.Serialize(text);
// %YAML 1.2\r\n
// ---\r\n
// |+2\r\n
//   abc\r
//     def\n
//   ghi\r\n
// ...\r\n

// 読み出しもそのままの形で行われます
restored = (string)serializer.Deserialize(yaml)[0];
// "abc\r  def\nghi\r\n"

// 出力された YAML テキストがエディタなどで編集され、
// 改行文字が変更されると、元の改行文字の情報は失われて
// しまうことに注意が必要です
yaml = yaml.Replace("\r\n", "\n").Replace("\r", "\n");
restored = (string)serializer.Deserialize(yaml)[0];
// "abc\n  def\nghi\n"

繰り返しますが、どちらの方法も YAML の規定に反する処理となります。これは、例の最後で示したように、改行文字がエディタやファイル転送などでファイルの改行文字が変更されてしまった場合にも、データ内容が影響を受けないように YAML が定義されているためです。

これらの回避策は多くの場面で便利に使うことができますが、出力される YAML の可搬性が犠牲になっていることに注意して下さい。

Last edited Oct 16, 2009 at 11:41 AM by osamu, version 9

Comments

No comments yet.