XAML へのシリアライズ (その 2)

前回の XAML へのシリアライズ (その 1) からの続きです。

前回は、ツリー状の構造になっているデータの集合を XAML にシリアライズする方法について記述しました。
つまり、あるオブジェクトの参照元はただ 1 つの親要素という前提で進めてきました。
しかし実は、XAML では任意の位置から他のオブジェクトを参照できます。
今回はその一例として、XAML をオブジェクト指向のデータストアとして利用する方法について考えます。

SQL Server をはじめとするリレーショナル データベースを利用する場合、
アプリケーション側で定義された .NET などのクラス、すなわちオブジェクト データモデルを、
リレーショナル データモデルに変換する必要がありました。

例えば、多対多の関連や配列をリレーショナル データベースに保存するには、便宜的にテーブルを追加するなどの措置が必要となります。
それに対してオブジェクト指向データベースの場合は、オブジェクト データモデルの形式のままでデータを保存できます。

 

では、オブジェクト指向のデータストアとして利用する具体例を示していきます。
概念データモデルが次の図で表されるとします。
これには、1 対多、多対多、継承の関係が含まれています。

概念データモデル

 

前回に示した方法で、この概念データモデルをクラスとして定義していきます。
ADO.NET Entity Framework の Code First でデータモデルを定義するのと同じ感覚で、次のようにできます。

UsersData.cs


using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Markup;

namespace XamlClassLib
{
    public class UsersData
    {
        Collection<Country> countries = new Collection<Country>();
        public Collection<Country> Countries { get { return countries; } }
        Collection<Group> groups = new Collection<Group>();
        public Collection<Group> Groups { get { return groups; } }
        Collection<User> users = new Collection<User>();
        public Collection<User> Users { get { return users; } }
    }

    [DebuggerDisplay(@"\{{Name}\}")]
    [ContentProperty("Users")]
    public class Country
    {
        public string Name { get; set; }
        Collection<User> users = new Collection<User>();
        public Collection<User> Users { get { return users; } }
    }

    [DebuggerDisplay(@"\{{Name}\}")]
    [ContentProperty("Users")]
    public class Group
    {
        public string Name { get; set; }
        Collection<User> users = new Collection<User>();
        public Collection<User> Users { get { return users; } }
    }

    [DebuggerDisplay(@"\{{GetType().Name}: {Name}\}")]
    [ContentProperty("Groups")]
    public class User
    {
        public string Name { get; set; }
        public Country Country { get; set; }
        Collection<Group> groups = new Collection<Group>();
        public Collection<Group> Groups { get { return groups; } }
    }

    public class SpecialUser : User
    {
        [DefaultValue(typeof(decimal), "0")]
        public decimal Discount { get; set; }
    }

    public class TrialUser : User
    {
        public DateTime Expiration { get; set; }
    }
}


 

さて、実際のデータが次のオブジェクト図で表されるとします。

オブジェクト図

 

コンソール アプリケーション プロジェクトでこれらのオブジェクトを構築して、XAML にシリアライズします。

Program.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Xaml;
using XamlClassLib;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var japan = new Country { Name = "Japan" };
            var us = new Country { Name = "US" };
            var admins = new Group { Name = "Admins" };
            var customers = new Group { Name = "Customers" };
            var taro = new User { Name = "Taro" };
            var jiro = new SpecialUser { Name = "Jiro", Discount = 0.3m };
            var hanako = new TrialUser { Name = "Hanako", Expiration = new DateTime(2014, 1, 1) };

            taro.Country = japan;
            japan.Users.Add(taro);
            jiro.Country = us;
            us.Users.Add(jiro);
            hanako.Country = japan;
            japan.Users.Add(hanako);

            taro.Groups.Add(admins);
            admins.Users.Add(taro);
            taro.Groups.Add(customers);
            customers.Users.Add(taro);
            jiro.Groups.Add(customers);
            customers.Users.Add(jiro);

            var data = new UsersData();
            data.Countries.Add(japan);
            data.Countries.Add(us);
            data.Groups.Add(admins);
            data.Groups.Add(customers);
            data.Users.Add(taro);
            data.Users.Add(jiro);
            data.Users.Add(hanako);

            XamlServices.Save("UsersData.xaml", data);
        }
    }
}


 

すると、次の XAML ファイルが出力されます。

UsersData.xaml


<UsersData xmlns="http://schemas.saka-pon.net/xamlsample" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <UsersData.Countries>
    <Country x:Name="__ReferenceID3" Name="Japan">
      <User Country="{x:Reference __ReferenceID3}" x:Name="__ReferenceID0" Name="Taro">
        <Group x:Name="__ReferenceID5" Name="Admins">
          <x:Reference>__ReferenceID0</x:Reference>
        </Group>
        <Group x:Name="__ReferenceID1" Name="Customers">
          <x:Reference>__ReferenceID0</x:Reference>
          <SpecialUser x:Name="__ReferenceID2" Discount="0.3" Name="Jiro">
            <SpecialUser.Country>
              <Country x:Name="__ReferenceID4" Name="US">
                <x:Reference>__ReferenceID2</x:Reference>
              </Country>
            </SpecialUser.Country>
            <x:Reference>__ReferenceID1</x:Reference>
          </SpecialUser>
        </Group>
      </User>
      <TrialUser Country="{x:Reference __ReferenceID3}" x:Name="__ReferenceID6" Expiration="2014-01-01" Name="Hanako" />
    </Country>
    <x:Reference>__ReferenceID4</x:Reference>
  </UsersData.Countries>
  <UsersData.Groups>
    <x:Reference>__ReferenceID5</x:Reference>
    <x:Reference>__ReferenceID1</x:Reference>
  </UsersData.Groups>
  <UsersData.Users>
    <x:Reference>__ReferenceID0</x:Reference>
    <x:Reference>__ReferenceID2</x:Reference>
    <x:Reference>__ReferenceID6</x:Reference>
  </UsersData.Users>
</UsersData>


 

x:Name でオブジェクトに名前を付け、x:Reference でオブジェクトを参照していることがわかります。
もちろん、XamlServices.Load メソッドでデシリアライズすれば元のオブジェクトに戻ります。

上記の XAML ではオブジェクト ツリーが再帰的に出力されているため可読性は高くないですが、これは下記に示す XAML と同等です。
デシリアライズすると同一のオブジェクトが得られます。


<UsersData xmlns="http://schemas.saka-pon.net/xamlsample" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UsersData.Countries>
        <Country x:Name="Japan" Name="Japan">
            <x:Reference>Taro</x:Reference>
            <x:Reference>Hanako</x:Reference>
        </Country>
        <Country x:Name="US" Name="US">
            <x:Reference>Jiro</x:Reference>
        </Country>
    </UsersData.Countries>
    <UsersData.Groups>
        <Group x:Name="Admins" Name="Admins">
            <x:Reference>Taro</x:Reference>
        </Group>
        <Group x:Name="Customers" Name="Customers">
            <x:Reference>Taro</x:Reference>
            <x:Reference>Jiro</x:Reference>
        </Group>
    </UsersData.Groups>
    <UsersData.Users>
        <User x:Name="Taro" Name="Taro" Country="{x:Reference Japan}">
            <x:Reference>Admins</x:Reference>
            <x:Reference>Customers</x:Reference>
        </User>
        <SpecialUser x:Name="Jiro" Name="Jiro" Country="{x:Reference US}" Discount="0.3">
            <x:Reference>Customers</x:Reference>
        </SpecialUser>
        <TrialUser x:Name="Hanako" Name="Hanako" Country="{x:Reference Japan}" Expiration="2014-01-01" />
    </UsersData.Users>
</UsersData>


この形式であれば Program.cs でのオブジェクト操作に対応しており、
オブジェクト指向データベースの構造としてイメージしやすいでしょう。

 

バージョン情報
.NET Framework 4.5

カテゴリー: .NET Framework. タグ: . Leave a Comment »

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。