2017-05-22 2 views
0

XMLシリアル化が自分の設計で達成可能かどうかを誰かにアドバイスできますか?XMLシリアル化を使用してXMLとしてC#オブジェクトデータを格納する方法

「システム管理」アプリケーションのコア機能のコードを記述しました。私のプロジェクトのSprint 1では、すべてのデータをSprint 2のADO.Netを使用して置き換える一時的なXMLデータストアに保存する必要があります。

以下の記事のようなXMLシリアル化を使用して、 https://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part https://www.codeproject.com/Articles/487571/XML-Serialization-and-Deserialization-Part-2

I(私は思う)以下のクラスは、XMLとして保存する必要があります。文字列で複数のプロパティ

  • UserAccessGroupクラスと

    • Userクラスプロパティ名とintのリストUserID
    • すべてのUserオブジェクトと1つ以上のUserAccessGroupオブジェクト(コンストラクタはparams UserAccessGroup []を使用)のListを保持するUserAdministrationクラス。これには、システム上でユーザーを追加/削除する方法、UserAccessGroupからユーザーIDを追加/削除する方法があります。
    • UserAdministrationオブジェクトと、ログオンしているアプリケーションのUserAccessGroupオブジェクトを保持するUserVerificationクラス。ログオンの失敗と成功のカウントには2つのintがあり、ログオンするメソッドはすべての条件が満たされるとtrueを返し、そうでない場合はfalseを返します。
    • 複数のプロパティを持つServiceRequestクラス
    • すべてのServiceRequestオブジェクトのリストを保持し、それらを追加/削除/編集するメソッドを持つServiceRequestTrackerクラス。

    私は私のプログラムファイル内に以下のコードがあります。

    static void Main() 
         { 
          UserAccessGroup SystemAdmin_App = new UserAccessGroup("Admin Operators"); 
          UserAccessGroup Shareholder_App = new UserAccessGroup("Shareholders"); 
          UserAccessGroup Broker_App = new UserAccessGroup("Brokers"); 
          UserAccessGroup StockExMgr_App = new UserAccessGroup("StockExMgrs"); 
    
          UserIDGenerator IDGenerator = new UserIDGenerator(); 
    
          UserAdministration userAdmin = new UserAdministration(IDGenerator, 
           SystemAdmin_App, Shareholder_App, Broker_App, StockExMgr_App); 
    
          UserVerificationService userVerification = new UserVerificationService(
           userAdmin, SystemAdmin_App); 
    
          Application.EnableVisualStyles(); 
          Application.SetCompatibleTextRenderingDefault(false); 
          LoginScreen myLoginScreen = new LoginScreen(); 
          Application.Run(myLoginScreen); 
         } 
    

    を今だけ私はTDDを使用して私のクラスのすべてを書き終わったことを私はXMLとしてオブジェクトデータを格納するに見てきました。 XMLシリアライゼーションを自分のデザインに適用するのが非常に混乱していることがわかりました。私のアプリケーションを完全にゼロから再設計せずにこれを達成することが可能かどうか疑問に思っています。

    ここにお手伝いいただければ幸いです。

  • +0

    既存のXMLシリアライザを使用している場合、通常、クラスに '[Serializable]'属性を置き、オブジェクトをシリアライザに渡すだけで十分です。特に、後で同じアプリケーションでオブジェクトの逆シリアル化を行う場合のみです。注意点:循環参照を引き起こす可能性のあるプロパティを '[XmlIgnore]'する必要があります。例えば。 ParentにChildプロパティがあり、そのChildオブジェクトに同じ接続を参照するParentプロパティがある場合。シリアライザはループでスタックし、スタックオーバーフローを引き起こします。 – Flater

    +0

    @Flater返信いただきありがとうございます!私は私のオブジェクトをシリアライズする方法はまだ分かりません。たとえば、UserAdministrationにはUsersのリストが含まれており、リストにユーザーを追加/削除するためのメソッドがあります。たとえば、UserAdministrationのaddUserメソッドを呼び出して新しいユーザーを追加します。新しいUserオブジェクトはどのようにしてXMLファイルに追加されますか? –

    +0

    ルート要素が配列でない限り、任意のクラスのセットを直列化できます。ルート要素はuserAdminです。したがって、userAdminをシリアル化して、自動的に他のクラスのxmlを作成します。 – jdweng

    答えて

    1

    コメント内の議論に従って、カスタムクラスを迅速に(デ)シリアライズする方法の基本的な概要を説明します。返事をありがとう@Flater

    :参考

    は、ここにあなたの私が返信てるためのコメントです!私は私のオブジェクトをシリアライズする方法はまだ分かりません。たとえば、UserAdministrationにはUsersのリストが含まれており、リストにユーザーを追加/削除するためのメソッドがあります。たとえば、UserAdministrationのaddUserメソッドを呼び出して新しいユーザーを追加します。新しいUserオブジェクトはどのようにしてXMLファイルに追加されますか?それはそんなにクリーンなコードになりますので、


    は、私はいつもこのヘルパークラスを使用します。私はどこかでインターネットからコピーしました。潜在的にStackOverflowです。私はそれが誰であるかを知っている/覚えていれば著者に賞賛します。

    public static class SerializerHelper 
    { 
        /// <summary> 
        /// Serializes an object. 
        /// </summary> 
        /// <typeparam name="T"></typeparam> 
        /// <param name="serializableObject"></param> 
        /// <param name="fileName"></param> 
        public static void SerializeObject<T>(string filepath, T serializableObject) 
        { 
         if (serializableObject == null) { return; } 
    
         try 
         { 
          XmlDocument xmlDocument = new XmlDocument(); 
          XmlSerializer serializer = new XmlSerializer(serializableObject.GetType()); 
          using (MemoryStream stream = new MemoryStream()) 
          { 
           serializer.Serialize(stream, serializableObject); 
           stream.Position = 0; 
           xmlDocument.Load(stream); 
           xmlDocument.Save(filepath); 
           stream.Close(); 
          } 
         } 
         catch (Exception ex) 
         { 
          //Log exception here 
         } 
        } 
    
    
        /// <summary> 
        /// Deserializes an xml file into an object list 
        /// </summary> 
        /// <typeparam name="T"></typeparam> 
        /// <param name="fileName"></param> 
        /// <returns></returns> 
        public static T DeSerializeObject<T>(string filepath) 
        { 
         T objectOut = default(T); 
    
         if (!System.IO.File.Exists(filepath)) return objectOut; 
    
         try 
         { 
          string attributeXml = string.Empty; 
    
          XmlDocument xmlDocument = new XmlDocument(); 
          xmlDocument.Load(filepath); 
          string xmlString = xmlDocument.OuterXml; 
    
          using (StringReader read = new StringReader(xmlString)) 
          { 
           Type outType = typeof(T); 
    
           XmlSerializer serializer = new XmlSerializer(outType); 
           using (XmlReader reader = new XmlTextReader(read)) 
           { 
            objectOut = (T)serializer.Deserialize(reader); 
            reader.Close(); 
           } 
    
           read.Close(); 
          } 
         } 
         catch (Exception ex) 
         { 
          //Log exception here 
         } 
    
         return objectOut; 
        } 
    } 
    

    私は使用例に入る前に、いくつかのヒントあなたは、このアプローチする方法を知っているので:

    • これらのメソッドは、指定したファイルに(あらゆるタイプの)単一のオブジェクトをシリアル化します。これはクラスFooList<Foo>、または保存するすべてのデータを含むカスタムメイドクラスです。MyFooData
    • 既存のファイルに追加することはできません。ファイルを保存するとき、古いファイルは上書きされます。私はまだファイルに追加する必要があるユースケースに遭遇しておらず、ファイルの内容を読み込んで変更し、オブジェクト全体を再度格納することができませんでした。しかし私は小規模なストレージにしか使用していません。
    • XMLシリアル化では、公開プロパティパラメータなしコンストラクタを使用します。だから、保存したいオブジェクトにこれらの両方があることを確認してください。プライベートプロパティまたは保護されたプロパティはシリアル化されません。またはパブリック/プライベート/保護されたクラスフィールド。
    • XMLシリアライザは自動的に、(パブリックプロパティの場合は)その中に含まれるサブクラスもシリアル化します。私が知っている限り、それはあなたが望むほど深く進むことができます。それはすべてを保存します。
    • XMLシリアライザの再帰性は弱いです。コード例で再帰を避ける方法を説明します。
    • XMLシリアル化では、格納にハッシュを使用するDictionary<T1,T2>またはIEnumerableをシリアル化できません。ただし、Dictionary<T1,T2>を保存する場合は、と入力してください。これはList<KeyValuePair<T1,T2>>として保存されます。つまり、データは保持されます。クイックルックアップに使用する内部ハッシュではありません。

    1 - Userクラスの[Serializable]属性の最も単純な例

    [Serializable] 
    public class User 
    { 
        public string Name { get; set; } 
        public string Email { get; set; } 
    } 
    
    public static void TestMethod() 
    { 
        var myUser = new User() { Name = "John", Email = "[email protected]" }; 
    
        //Save to file 
        SerializerHelper.SerializeObject(@"C:\MyDir\MyFile.txt", myUser); 
    
        //Read from file 
        var myUserReloaded = SerializerHelper.DeSerializeObject<User>(@"C:\MyDir\MyFile.txt"); 
    } 
    

    をメモします。これは通常、この作業を行うためにクラスに加える必要がある唯一の変更です。

    2 - 先端に述べたように再帰スタックを回避が

    をオーバーフロー、この直列化は再帰の弱点を有しています。これは、クラスが双方向で互いに参照するときに発生します。

    [Serializable] 
    public class User 
    { 
        public string Name { get; set; } 
        public string Email { get; set; } 
    
        public Manager Boss {get; set; } 
    } 
    
    [Serializable] 
    public class Manager 
    { 
        public string Name { get; set; } 
    
        public User FavoriteEmployee {get; set; } 
    } 
    
    public static void TestMethod() 
    { 
        var userJohn = new User() { Name = "John", Email = "[email protected]" }; 
        var managerMark = new Manager() { Name = "Mark" }; 
    
        managerMark.FavoriteEmployee = userJohn; 
        userJohn.Boss = managerMark; 
    
        //Save to file 
        SerializerHelper.SerializeObject(@"C:\MyDir\MyFile.txt", userJohn); 
    
        //Read from file 
        var userJohnReloaded = SerializerHelper.DeSerializeObject<User>(@"C:\MyDir\MyFile.txt"); 
    } 
    

    ファイルを保存するときに、シリアライザが無限ループで停止するため、スタックオーバーフロー例外が発生します。

    シリアライザはuserJohnのすべてのプロパティをファイルに書き込もうとします。 Bossプロパティに到達すると、それはシリアル化可能オブジェクトであり、managerMarkのすべてのプロパティのシリアル化が開始されます。 FavoriteEmployeeプロパティに到達すると、それはシリアル化可能オブジェクトであり、userJohnのすべてのプロパティのシリアル化が開始されます。ときに...

    [XmlIgnore]属性を使用して、2つのプロパティのいずれか(または両方、本当に安全にしたい場合)を防ぐことができます。

    [Serializable] 
    public class User 
    { 
        public string Name { get; set; } 
        public string Email { get; set; } 
    
        public Manager Boss {get; set; } 
    } 
    
    [Serializable] 
    public class Manager 
    { 
        public string Name { get; set; } 
    
        [XmlIgnore] 
        public User FavoriteEmployee {get; set; } 
    } 
    

    シリアライザは、ファイルにuserJohnのすべてのプロパティを記述しようとします。 Bossプロパティに到達すると、それはシリアル化可能オブジェクトであり、managerMarkのすべてのプロパティのシリアル化が開始されます。 FavoriteEmployeeプロパティに到達すると、このプロパティは[XmlIgnore]とマークされ、内部に含まれているものをシリアル化しようとはしません。


    この回答はあなたが探していたものです。


    EDIT私は大きな注意点を忘れてしまいました。

    Childというオブジェクトが2つあるList<Child>を保存しているとします。両方ともChildオブジェクトにはParentというプロパティがあり、両方の子が同じParent(メモリ内の同じオブジェクト)を参照しているだけです。

    私が保管している場合(その子のリストを含む)Parentそれがシリアライズされます私たちの両方Childオブジェクトが含まれますParent。デシリアライズするときは、最初にParentというオブジェクトと2つのオブジェクトがあります。私は(自分の親を含む)List<Child>を保存する場合

    することは、それは両方のChildオブジェクトに対してParentをシリアル化します。ただし、逆シリアル化の場合、Parentオブジェクトは逆シリアル化されますが、はメモリ内の別のオブジェクトになります

    ご覧のとおり、両方の子供が(メモリ内のオブジェクトとして)同じParentを参照することを期待している場合、ここに潜在的なバグが存在する可能性があります。このため、シリアル化を使用する方法、および[XmlIgnore]属性を配置する場所に関する個人的なルールがあります。

    子供(IEnumerable<Foo>)はシリアル化されていますが、親(Foo)は取得しません。これにより、ツリー全体を一度に保存することができます。

    これは私が親店に持っている(と、自動的にその子があまりにもシリアル化されてい)ことを意味し、(あなたがChildParentIdキーを保存しない)私は、その親への参照せずに子を保存することができます。

    このようにそれを行うことにより、あなたはすべてのオブジェクトのみをXMLファイルに一度を述べますので、あなたは、デシリアライズによってParentオブジェクトの多くを作成しないことを保証します。


    編集

    私は、メソッド呼び出しにSerializerHelper.を追加するのを忘れ。今修正されました。

    +0

    また、 'FavoriteEmployee'と' Boss'IDをユーザIDの使用を参照してみることもできます。 – Tvde1

    +0

    @ Tvde1:はい。ただし、ボスのIDのみを格納する場合は、ボスオブジェクトを別々にシリアル化する必要があります。また、データを再度読み込むときは、保存したIDに基づいて 'Boss'プロパティを手動で入力する必要があります。その代わりに(私の例のように一方向にのみ)オブジェクトをシリアル化していれば、逆シリアル化は必要なオブジェクトとそのデータで 'Boss'プロパティをすぐに埋めます。私の個人的なルールは:子(IEnumerable参照)はシリアル化され、親(単数参照)は取得されません。これにより、ツリー全体を一度に保存することができます。 – Flater

    +0

    @Flater答えがありがとう、本当に包括的です!私は今私のアプリケーションにこれを適用することができると思う(おそらくいくつかの試行錯誤で!)。 –

    関連する問題