none
XmlSerializer の デシリアライズ時の 親クラスの参照追加 RRS feed

  • 質問

  • 階層構造になったクラスを XmlSerializer で シリアライズして、再度デシリアライズしています。

    その際、下記のクラスですと、子クラスのParent の参照が復元できません。
    再度デシリアライズ時に、Add のメソッドが使用(呼び出)されていませんでした。

    一旦、別のクラスを作成して Foreach 等で ループしながら 復元したいクラスへプロパティ毎にコピーすれば可能ですが、
    もう少しスマートな方法はないでしょう?

    public class 検査条件
    {
        public string 検査名;
        public float 判定値;
    
        public ObservableCollection<検査点> 検査点一覧 = new ObservableCollection<検査点>();
                
        public void Add(検査点 obj)
        {
            obj.Parent = this;
            検査点一覧.Add(obj);
        }
    
        public class 検査点
        {
            [XmlIgnore]
            public 検査条件 Parent;
    
            public string 測定点;
            public float 測定値;
            public bool 合格
            {
                get
                {
                    bool ret = false;
                    if (測定値 >= Parent.判定値) { ret = true; }
                    return ret;
                }
            }
        }
    }
    // INotifyPropertyChangedインターフェースの実装は省略しています。
    

    2017年9月28日 2:33

回答

  • ObservableCollectionのコレクション変更イベントで親を設定するとか。

    class 検査条件 {
        pubilc ObservableCollection<検査点> 検査点一覧 { get; set; } = new  ObservableCollection<検査点>();
        public 検査条件() {
            this.検査点一覧.CollectionChanged += this.On検査点一覧Changed;
        }
        private void On検査点一覧Changed(object sender, NotifyCollectionChangedEventArgs e) {
            if (e.OldItems != null) {
                foreach (検査点 p in e.OldItems) p.Parent = null;
            }
            if (e.NewItems != null) {
                foreach (検査点 p in e.NewItems) p.Parent = this;
            }
        }
    }

    2017年9月28日 8:23
  • こんにちは。

    検査条件クラスにICollection<T>インターフェイスを実装するという方法はどうでしょうか。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    
    namespace ConsoleApp1
    {
        public class Program
        {
    
            public static void Main(string[] args)
            {
                var 検査条件 = new 検査条件();
                検査条件.Add(new 検査点() { 測定値 = 1 });
                検査条件.Add(new 検査点() { 測定値 = 2 });
                検査条件.Add(new 検査点() { 測定値 = 3 });
                var serializer = new XmlSerializer(typeof(検査条件));
                using (var stream = new MemoryStream())
                {
                    serializer.Serialize(stream, 検査条件);
                    stream.Position = 0;
                    検査条件 = (検査条件)serializer.Deserialize(stream);
                }
            }
    
        }
    
        public class 検査条件 : ICollection<検査点>
        {
    
            public string 検査名;
            public float 判定値;
            public ObservableCollection<検査点> 検査点一覧 = new ObservableCollection<検査点>();
    
            public int Count => 検査点一覧.Count;
    
            public bool IsReadOnly => false;
    
            public void Add(検査点 item)
            {
                item.Parent = this;
                検査点一覧.Add(item);
            }
    
            public void Clear()
            {
                検査点一覧.Clear();
            }
    
            public bool Contains(検査点 item)
            {
                return 検査点一覧.Contains(item);
            }
    
            public void CopyTo(検査点[] array, int arrayIndex)
            {
                検査点一覧.CopyTo(array, arrayIndex);
            }
    
            public IEnumerator<検査点> GetEnumerator()
            {
                return 検査点一覧.GetEnumerator();
            }
    
            public bool Remove(検査点 item)
            {
                return 検査点一覧.Remove(item);
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return 検査点一覧.GetEnumerator();
            }
    
        }
    
        public class 検査点
        {
            [XmlIgnore]
            public 検査条件 Parent;
    
            public string 測定点;
            public float 測定値;
            public bool 合格
            {
                get
                {
                    bool ret = false;
                    if (測定値 >= Parent.判定値) { ret = true; }
                    return ret;
                }
            }
        }
    
    }
    

    2017年9月28日 7:57
  • いろいろ手抜きできるObservableCollectionを作ってみるとか

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Xml.Serialization;
    namespace ConsoleApplication1
    {
        public class Program
        {
            static void Main(string[] args)
            {
                検査条件 data1 = CreateTest(1);
                検査条件 data2 = CreateTest(2);
    
                //親を自動的に変更してみるテスト
                data1.検査点一覧.Add(data2.検査点一覧[3]);
                data2.検査点一覧.Add(data1.検査点一覧[5]);
    
                SerializeTest(data1);
                SerializeTest(data2);
                Console.ReadLine();
            }
    
            private static 検査条件 CreateTest(int id)
            {
                var data = new 検査条件();
                data.検査名 = "親" + id;
                for (int i = 0; i < 10; i++)
                {
                    data.検査点一覧.Add(new 検査条件.検査点() { 測定値 = i, 測定点 = id + "-" + i.ToString() });
                }
                return data;
            }
            private static void SerializeTest(検査条件 data)
            {
                System.Xml.Serialization.XmlSerializer seri = new XmlSerializer(typeof(検査条件));
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                seri.Serialize(ms, data);
                ms.Position = 0;
    
                Console.WriteLine(data.検査名);
                var dataX = seri.Deserialize(ms) as 検査条件;
                foreach (検査条件.検査点 item in dataX.検査点一覧)
                {
                    Console.WriteLine("\t" + item.測定点 + "\t" + (item.Parent == null ? "NULL" : item.Parent.検査名));
                }
            }
    
            public class 検査条件 : IChildren<検査条件.検査点>
            {
                public 検査条件()
                {
                    //親を自動設定するObservableColelction
                    検査点一覧 = new ObservableCollection<検査点, 検査条件>(this);
                }
                public string 検査名;
                public float 判定値;
    
                public ObservableCollection<検査点> 検査点一覧 { get; private set; }
    
                ICollection<検査条件.検査点> IChildren<検査条件.検査点>.Children
                {
                    get { return 検査点一覧; }
                }
    
                //ObservableCollectionで自動処理するので要らない
                //public void Add(検査点 obj)
                //{
                //    obj.Parent = this;
                //    検査点一覧.Add(obj);
                //}
    
                public class 検査点 : IParent<検査条件>
                {
    
                    [XmlIgnore]
                    public 検査条件 Parent { get; set; }
    
                    public string 測定点;
                    public float 測定値;
                    public bool 合格
                    {
                        get
                        {
                            bool ret = false;
                            if (測定値 >= Parent.判定値) { ret = true; }
                            return ret;
                        }
                    }
                }
    
    
    
    
            }
        }
    
        interface IParent
        {
        }
    
        interface IParent<TParent> : IParent where TParent : class
        {
            TParent Parent { get; set; }
        }
        interface IChildren
        {
        }
        interface IChildren<TChild> where TChild : class
        {
            ICollection<TChild> Children { get; }
        }
    
        /// <summary>親子関係を自動適用するObservablecollection</summary>
        /// <typeparam name="TChild">子とするクラスの型</typeparam>
        /// <typeparam name="TParent">親とするクラスの型</typeparam>
        class ObservableCollection<TChild, TParent>
            : ObservableCollection<TChild>
            where TChild : class,IParent<TParent>
            where TParent : class
        {
            public ObservableCollection(TParent parent)
            {
                this.Parent = parent;
            }
            public TParent Parent
            {
                get
                {
                    return _Parent;
                }
                set
                {
                    this._Parent = value;
                    foreach (TChild child in this)
                    {
                        child.Parent = value;
                    }
                }
    
            } private TParent _Parent;
    
            protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                if (e.OldItems != null)
                {
                    foreach (TChild child in e.OldItems)
                    {
                        child.Parent = null;
                    }
                }
                if (e.NewItems != null)
                {
                    foreach (TChild child in e.NewItems)
                    {
                        var ic = child.Parent as IChildren<TChild>;
                        if (ic != null)
                        {
                            //別の親に移動したら元の親子から取り除く
                            ic.Children.Remove(child);
                        }
                        child.Parent = this.Parent;
                    }
                }
                base.OnCollectionChanged(e);
            }
        }
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年9月28日 8:48

すべての返信

  • こんにちは。

    検査条件クラスにICollection<T>インターフェイスを実装するという方法はどうでしょうか。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml.Serialization;
    
    namespace ConsoleApp1
    {
        public class Program
        {
    
            public static void Main(string[] args)
            {
                var 検査条件 = new 検査条件();
                検査条件.Add(new 検査点() { 測定値 = 1 });
                検査条件.Add(new 検査点() { 測定値 = 2 });
                検査条件.Add(new 検査点() { 測定値 = 3 });
                var serializer = new XmlSerializer(typeof(検査条件));
                using (var stream = new MemoryStream())
                {
                    serializer.Serialize(stream, 検査条件);
                    stream.Position = 0;
                    検査条件 = (検査条件)serializer.Deserialize(stream);
                }
            }
    
        }
    
        public class 検査条件 : ICollection<検査点>
        {
    
            public string 検査名;
            public float 判定値;
            public ObservableCollection<検査点> 検査点一覧 = new ObservableCollection<検査点>();
    
            public int Count => 検査点一覧.Count;
    
            public bool IsReadOnly => false;
    
            public void Add(検査点 item)
            {
                item.Parent = this;
                検査点一覧.Add(item);
            }
    
            public void Clear()
            {
                検査点一覧.Clear();
            }
    
            public bool Contains(検査点 item)
            {
                return 検査点一覧.Contains(item);
            }
    
            public void CopyTo(検査点[] array, int arrayIndex)
            {
                検査点一覧.CopyTo(array, arrayIndex);
            }
    
            public IEnumerator<検査点> GetEnumerator()
            {
                return 検査点一覧.GetEnumerator();
            }
    
            public bool Remove(検査点 item)
            {
                return 検査点一覧.Remove(item);
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return 検査点一覧.GetEnumerator();
            }
    
        }
    
        public class 検査点
        {
            [XmlIgnore]
            public 検査条件 Parent;
    
            public string 測定点;
            public float 測定値;
            public bool 合格
            {
                get
                {
                    bool ret = false;
                    if (測定値 >= Parent.判定値) { ret = true; }
                    return ret;
                }
            }
        }
    
    }
    

    2017年9月28日 7:57
  • ObservableCollectionのコレクション変更イベントで親を設定するとか。

    class 検査条件 {
        pubilc ObservableCollection<検査点> 検査点一覧 { get; set; } = new  ObservableCollection<検査点>();
        public 検査条件() {
            this.検査点一覧.CollectionChanged += this.On検査点一覧Changed;
        }
        private void On検査点一覧Changed(object sender, NotifyCollectionChangedEventArgs e) {
            if (e.OldItems != null) {
                foreach (検査点 p in e.OldItems) p.Parent = null;
            }
            if (e.NewItems != null) {
                foreach (検査点 p in e.NewItems) p.Parent = this;
            }
        }
    }

    2017年9月28日 8:23
  • いろいろ手抜きできるObservableCollectionを作ってみるとか

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Xml.Serialization;
    namespace ConsoleApplication1
    {
        public class Program
        {
            static void Main(string[] args)
            {
                検査条件 data1 = CreateTest(1);
                検査条件 data2 = CreateTest(2);
    
                //親を自動的に変更してみるテスト
                data1.検査点一覧.Add(data2.検査点一覧[3]);
                data2.検査点一覧.Add(data1.検査点一覧[5]);
    
                SerializeTest(data1);
                SerializeTest(data2);
                Console.ReadLine();
            }
    
            private static 検査条件 CreateTest(int id)
            {
                var data = new 検査条件();
                data.検査名 = "親" + id;
                for (int i = 0; i < 10; i++)
                {
                    data.検査点一覧.Add(new 検査条件.検査点() { 測定値 = i, 測定点 = id + "-" + i.ToString() });
                }
                return data;
            }
            private static void SerializeTest(検査条件 data)
            {
                System.Xml.Serialization.XmlSerializer seri = new XmlSerializer(typeof(検査条件));
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                seri.Serialize(ms, data);
                ms.Position = 0;
    
                Console.WriteLine(data.検査名);
                var dataX = seri.Deserialize(ms) as 検査条件;
                foreach (検査条件.検査点 item in dataX.検査点一覧)
                {
                    Console.WriteLine("\t" + item.測定点 + "\t" + (item.Parent == null ? "NULL" : item.Parent.検査名));
                }
            }
    
            public class 検査条件 : IChildren<検査条件.検査点>
            {
                public 検査条件()
                {
                    //親を自動設定するObservableColelction
                    検査点一覧 = new ObservableCollection<検査点, 検査条件>(this);
                }
                public string 検査名;
                public float 判定値;
    
                public ObservableCollection<検査点> 検査点一覧 { get; private set; }
    
                ICollection<検査条件.検査点> IChildren<検査条件.検査点>.Children
                {
                    get { return 検査点一覧; }
                }
    
                //ObservableCollectionで自動処理するので要らない
                //public void Add(検査点 obj)
                //{
                //    obj.Parent = this;
                //    検査点一覧.Add(obj);
                //}
    
                public class 検査点 : IParent<検査条件>
                {
    
                    [XmlIgnore]
                    public 検査条件 Parent { get; set; }
    
                    public string 測定点;
                    public float 測定値;
                    public bool 合格
                    {
                        get
                        {
                            bool ret = false;
                            if (測定値 >= Parent.判定値) { ret = true; }
                            return ret;
                        }
                    }
                }
    
    
    
    
            }
        }
    
        interface IParent
        {
        }
    
        interface IParent<TParent> : IParent where TParent : class
        {
            TParent Parent { get; set; }
        }
        interface IChildren
        {
        }
        interface IChildren<TChild> where TChild : class
        {
            ICollection<TChild> Children { get; }
        }
    
        /// <summary>親子関係を自動適用するObservablecollection</summary>
        /// <typeparam name="TChild">子とするクラスの型</typeparam>
        /// <typeparam name="TParent">親とするクラスの型</typeparam>
        class ObservableCollection<TChild, TParent>
            : ObservableCollection<TChild>
            where TChild : class,IParent<TParent>
            where TParent : class
        {
            public ObservableCollection(TParent parent)
            {
                this.Parent = parent;
            }
            public TParent Parent
            {
                get
                {
                    return _Parent;
                }
                set
                {
                    this._Parent = value;
                    foreach (TChild child in this)
                    {
                        child.Parent = value;
                    }
                }
    
            } private TParent _Parent;
    
            protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                if (e.OldItems != null)
                {
                    foreach (TChild child in e.OldItems)
                    {
                        child.Parent = null;
                    }
                }
                if (e.NewItems != null)
                {
                    foreach (TChild child in e.NewItems)
                    {
                        var ic = child.Parent as IChildren<TChild>;
                        if (ic != null)
                        {
                            //別の親に移動したら元の親子から取り除く
                            ic.Children.Remove(child);
                        }
                        child.Parent = this.Parent;
                    }
                }
                base.OnCollectionChanged(e);
            }
        }
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2017年9月28日 8:48
  • ご回答を頂きありがとうございます。
    お礼が遅くなり申し訳ありませんでした。

    標準的な機能は無いという事が分かったのが収穫です。
    ”出来ない事”を調べるのは難しいです。

    頂いた回答ではC#初心者(VB経験者)にとっては、
    Hongliang様のイベント処理が分かり易くコードもすぐに理解できました。
    今回はこの方法で進めていこうと思います。

    将来的にはTakashi Shinohara様やgekka様のコードも書けるようになりたいです。
    その為にはインターフェースの理解をしないといけないですね。

    特にUIをXAMLで書いていますので、
    gekka様のObservableCollection を用意するのも良いですね。

    今後も機会がありましたらよろしくお願いします。

    2017年10月4日 10:17