DCSIMG
שיטוח של אובייקטים לקובץ - כתיבה לקובץ וקריאה מקובץ - שלמה גולדברג (הרב דוטנט)

שלמה גולדברג (הרב דוטנט)

מרצה בסלע ויועץ בעולם ה - net.

שיטוח של אובייקטים לקובץ - כתיבה לקובץ וקריאה מקובץ

 

הפעם אני רוצה לכתוב פוסט לטובת אלה שנכנסים לעולם ה - Net.
יצא לי בזמן האחרון כמה פעמים להדגים קריאה וכתיבה לקובץ (סירילזיציה ודיסיריליזציה), חשבתי שכדאי לכתוב פוסט על זה, כי מן הסתם יהיו עוד אנשים שלומדים ומחפשים הסבר על הנושא.
 
 
הרעיון בלקחת אובייקט ולשמור אותו בקובץ הוא כדי שנוכל לשמור על המצב של האפליקציה, נניח שיש לנו תוכנה לניהול עובדים, כשהתוכנה תיסגר כל הנתונים שיש לנו בזיכרון יעלמו, ולכן אנחנו רוצים לשמור את הנתונים שלנו בדיסק הקשיח, ולמעשה לשטח את האובייקטים שלנו מהזיכרון הנדיף לזיכרון קבוע - מה שנקרא Serialization.
 
 את דוגמת הקוד אפשר להוריד כאן.
 
אז איך מבצעים את זה,
 
הדרך המורכבת והמסובכת היא - לכתוב לבד את כל המאפיינים של האובייקט לקובץ, זה לא מומלץ כי נצטרך כל הזמן לתחזק את הקוד הזה, כלומר במידה ויש לנו מאפיין חדש נצטרך ללכת לשנות את הקוד של הכתיבה לקובץ.
בכל מקרה אני אדגים גם את השיטה הזאת, דרך נוספת היא להשתמש במנגנונים מובנים של Net, ונוכל לבחור מתוך כמה מימושים של Net. אני אדגים כאן כרגע שניים, הראשון יהיה XmlSerializer והשני יהיה BinaryFormatter. יש עוד כמה, על אחד נוסף (DataContractSerializer) תוכלו לקרוא כאן.
 
נניח שזה מבנה האובייקטים שלנו.
 

public abstract class Employee

{

    public int Id { get; set; }

    public string Name { get; set; }

}

 

public class Programer : Employee

{

    public bool KnowCSharp { get; set; }

}

 

public class Manager : Employee

{

    public int NumberOfProject { get; set; }

}

 
הנה ה - Main שלנו.
 

static void Main(string[] args)

{

    List<Employee> employees = new List<Employee>();

    employees.Add(new Programer() { Id = 1, Name = "Shlomo", KnowCSharp = true });

    employees.Add(new Programer() { Id = 2, Name = "Pini", KnowCSharp = true });

    employees.Add(new Manager() { Id = 1, Name = "Alon", NumberOfProject = 100 });

 

}

 
כעת אנחנו רוצים לשמור את ה - List שלנו בקובץ.
 
הדוגמא הראשונה ממומשת על ידי המתכנת בכתיבה עצמאית לקובץ. (אני לא מדגים כאן כתיבת OO נכונה - כי חלק מהפעולות היו צריכים להיות בתוך המחלקות)
 
כתיבה לקובץ דוגמא 1.
 

static void WriteToFileCustom(List<Employee> employees)

{

    using (StreamWriter writer = File.AppendText(@"C:\a.txt"))

    {

        foreach (var item in employees)

        {

            Programer programer = item as Programer;

            if (programer != null)

            {

                writer.WriteLine("Programer,{0},{1},{2}",

                    programer.Id, programer.Name, programer.KnowCSharp);

                continue;

            }

 

            Manager manager = item as Manager;

            if (manager != null)

            {

                writer.WriteLine("Manager,{0},{1},{2}",

                    manager.Id, manager.Name, manager.NumberOfProject);

                continue;

            }

        }

    }

}

 
 
 וכעת בקובץ יופיע לנו:
 
Programer,1,Shlomo,True
Programer,2,Pini,True
Manager,1,Alon,100
 
כעת נראה איך אפשר לשחזר את המידע מתוך הקובץ.
 

static List<Employee> ReadFromFileCustom()

{

    List<Employee> employees = new List<Employee>();

 

    string[] lines = File.ReadAllLines(@"C:\a.txt");

    foreach (var item in lines)

    {

        string[] props = item.Split(',');

        Employee employee = null;

 

        if (props[0] == "Programer")

        {

            Programer programer = new Programer();

            programer.KnowCSharp = bool.Parse(props[3]);

 

            employee = programer;

        }

        else

        {

            Manager manager = new Manager();

            manager.NumberOfProject = int.Parse(props[3]);

 

            employee = manager;

        }

 

        employee.Id = int.Parse(props[1]);

        employee.Name = props[2];

 

        employees.Add(employee);

    }

 

    return employees;

}

 

 
כמו שכתבתי זו אינה הדרך הממולצת, עדיף להשתמש עם מנגנון מובנה.
 
והנה קוד שמדגים שימוש ב - XmlSerializer.
 
כדי להשתמש בזה אנחנו צריכים לעמוד בשני תנאים.
1. שיהיה לנו בנאי ללא פרמטרים.
2. להוסיף מעל כל המחלקות שלנו את ה - Attribute של Serializable
3. להוסיף מעל האבא את כל הבנים שלו. שיכולים לעבור סירליזציה דרכו.
 
ולכן המחלקות שלנו יראו כך:
 

[Serializable]

[XmlInclude(typeof(Programer))]

[XmlInclude(typeof(Manager))]

public abstract class Employee

{

    public int Id { get; set; }

    public string Name { get; set; }

 

    public Employee()

    {

 

    }

}

 

[Serializable]

public class Programer : Employee

{

    public bool KnowCSharp { get; set; }

 

    public Programer()

    {

 

    }

}

 

[Serializable]

public class Manager : Employee

{

    public int NumberOfProject { get; set; }

 

    public Manager()

    {

 

    }

}

 
 
עכשיו נוסיף מתודה לשמירה לקובץ.
 

static void WriteToFileXml(List<Employee> employees)

{

    XmlSerializer serializer = new XmlSerializer(typeof(List<Employee>));

    using (StreamWriter writer = File.AppendText(@"C:\a.xml"))

    {

        serializer.Serialize(writer, employees);

    }

}

 
כמו שאפשר לראות, הקוד הוא הרבה יותר פשוט ולא צריך לתחזק אותו בכל פעם שמשנים את האובייקטים.
הנה הקובץ שנוצר:
 

<?xml version="1.0" encoding="utf-8"?>

<ArrayOfEmployee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <Employee xsi:type="Programer">

    <Id>1</Id>

    <Name>Shlomo</Name>

    <KnowCSharp>true</KnowCSharp>

  </Employee>

  <Employee xsi:type="Programer">

    <Id>2</Id>

    <Name>Pini</Name>

    <KnowCSharp>true</KnowCSharp>

  </Employee>

  <Employee xsi:type="Manager">

    <Id>1</Id>

    <Name>Alon</Name>

    <NumberOfProject>100</NumberOfProject>

  </Employee>

</ArrayOfEmployee>

 
והנה המתודה לשיחזור הנתונים מהקובץ.
 

static List<Employee> ReadFromFileXml()

{

    XmlSerializer serializer = new XmlSerializer(typeof(List<Employee>));

    using (StreamReader reader = new StreamReader(@"C:\a.xml"))

    {

        return (List<Employee>)serializer.Deserialize(reader);

    }

 

}

 
 
דרך נוספת שאני רוצה להראות, היא באמצעות סירליזציה בינארית. כלומר - לקחת את מקטע הזיכרון של המידע ולעשות לו סירליזציה (ולשמור את המידע הבינארי בקובץ)
במקרה הזה מה שצריך זה רק את ה - Attribute של Serializable. אבל לא צריך בנאי ריק וגם לא צריך את המידע אודות כל הבנים (כי אנחנו מעבירים בלוק של זיכרון).
 
הנה המתודה לכתיבה.
 

static void WriteToFileBinary(List<Employee> employees)

{

    BinaryFormatter binary = new BinaryFormatter();

    using (Stream writer = new FileStream(@"C:\c.txt", FileMode.Create))

    {

        binary.Serialize(writer, employees);

    }

}

 
 
אני לא יכול להעתיק לכאן את מה שנוצר היות שזה מידע בינארי.
 
הנה הקוד לשיחזור.
 

static List<Employee> ReadFromFileBinary()

{

    BinaryFormatter binary = new BinaryFormatter();

    using (Stream reader = new FileStream(@"C:\c.txt", FileMode.Open))

    {

        return (List<Employee>)binary.Deserialize(reader);

    }

}

 
 

תוכן התגובה

Guy Burstein כתב/ה:

רק כדי להשלים את התמונה, שווה להסתכל על DataContractSerialization:

blogs.microsoft.co.il/.../WCF-Serialization.aspx

blogs.microsoft.co.il/.../WCF-Serialization-Same-Objects.aspx

וב- Framework 4.0 את השימוש ב- XamlServices:

blogs.microsoft.co.il/.../xaml-in-net-4-0-serialization-and-deserialization-using-xamlservices.aspx

מלבד זה - אתה תותח!

# July 23, 2009 9:44 AM

Shlomo כתב/ה:

תודה :)

# July 23, 2009 11:37 AM

Rotem Bloom כתב/ה:

אחלה של פוסט

# July 23, 2009 6:36 PM

Liran Chen כתב/ה:

חשוב לציין שהסיראליזציה המובנית בדוט נט היא מאוד איטית ורחוקה מלהיות יעילה. לכן לכל מי שהעניין הזה מדאיג אותו, עדיף שיכתוב רכיב משלו שיעשה את העבודה הזאת במקום דוט נט

# July 23, 2009 7:29 PM

Pini Dayan כתב/ה:

אתה לא מתבייש לקחת רעיונות מפרוייקט הסיום של הסטודנטים שלי? :-)

# July 26, 2009 8:43 AM

Shlomo כתב/ה:

אם היית מסביר לסטודנטים שלך כמו שצריך, אני לא הייתי צריך לעשות את זה :)

# July 26, 2009 9:31 AM

יוסי גולדברג כתב/ה:

האובייקט List עצמו אינו [Serializable], או שאני טועה?

# November 17, 2009 9:55 AM

Shlomo כתב/ה:

הוא כן - אבל השאלה מה יש בתוך ה - List

# November 17, 2009 4:10 PM

מתכנתת מתנסה כתב/ה:

האם יש דרך לעשות את זה עם VAR - כלומר עם משתנה שיצרתי באמצעות LINQ ואין לי את הסוג שלו?

# October 25, 2010 2:42 PM

שלמה גולדברג (הרב דוטנט) כתב/ה:

יצא לי לכתוב מחלקה שנראית כך: public struct EnviormentColor { public string Name { get ; set ; } public

# February 19, 2012 12:25 AM
שלח תגובה

(שדה חובה)  

(שדה חובה)  

(אופציונלי)

(שדה חובה) 

Please add 8 and 6 and type the answer here:


Enter the numbers above: