serilization לכל התוכנה
-
מה שעשיתי בסופו של דבר:
כתבתי מחלקה של סריליזציה ודסריליזציה,
אני נותן למשתמש לבחור את התיקייה הרצויה עליו (או ליצור חדשה) ובה נשמרים הנתונים.
בתיקייה שהוא בחר אני יוצר (מאחורי הקלעים) עוד 2 תיקיות משנה: userdata ו data כמו שרחמים המליץ.
בתוך התיקייה דטה אני שומר כל ליסט בתור קובץ נפרד.
בפתיחה של הקבצים, הוא בוחר את התיקייה הראשית שהוא יצר, ואני לבד פותח את הקבצים מתוך תיקיות המשנה.כתבתי את הקוד הבא: (אזהרה - קצת ארוך :lol: )
public static class SerilizationAll { //סריליזציה של כל התכנית public static void Serilization() { FolderBrowserDialog folderBrowser = new FolderBrowserDialog(); if (folderBrowser.ShowDialog() == DialogResult.OK) { string folder = folderBrowser.SelectedPath; folder += @"\userData\data";//מוסיף לנתיב תיקיית משנה לשמירת הנתונים DirectoryInfo directoryInfo = new DirectoryInfo(folder); directoryInfo.Create();//יוצר את ספריות המשנה serilizationList(folder);//קורא למתודה שמבצעת את הסריליזציה } } //סריליזציה של כל הליסטים מקבלת את הערך של התיקייה לשמירת הנתונים public static void serilizationList(string folder) { //יוצר את שמות הקבצים עבור כל הליסטים string fileDonor = folder; string fileDonation = folder; string fileCredit = folder; string fileBank = folder; fileDonor += @"\donorList"; fileDonation += @"\donationList"; fileCredit += @"\creditCardList"; fileBank += @"\bankAccountList"; //יוצר אובייקטים המקבלים את רשימות הליסטים הקיימים בתוכנית var donationObject = AddTorem.donations; var donorsObject = AddTorem.donors; var creditObject = AddTorem.credits; var bankObjects = AddTorem.accounts; //סריאליזציה עבור כל ליסט לקובץ נפרד using (FileStream fs = new FileStream(fileDonor, FileMode.Create)) { XmlSerializer donorXmlS = new XmlSerializer(typeof(List<Donor>)); donorXmlS.Serialize(fs, donorsObject); } using (FileStream donatStr = new FileStream(fileDonation, FileMode.Create)) { XmlSerializer donationXmlS = new XmlSerializer(typeof(List<Donation>)); donationXmlS.Serialize(donatStr, donationObject); } using (FileStream creditStream = new FileStream(fileCredit, FileMode.Create)) { XmlSerializer creditXmlS = new XmlSerializer(typeof(List<CreditCard>)); creditXmlS.Serialize(creditStream, creditObject); } using (FileStream bankStream = new FileStream(fileBank, FileMode.Create)) { XmlSerializer bankXMLs = new XmlSerializer(typeof(List<BankAccount>)); bankXMLs.Serialize(bankStream, bankObjects); } } //deserilization for all program public static void Deserilization() { FolderBrowserDialog folderBrowser = new FolderBrowserDialog(); if (folderBrowser.ShowDialog() == DialogResult.OK) { string folders = folderBrowser.SelectedPath; folders += @"\userData\data";//מוסיף לנתיב את הנתיב לתיקיית משנה בה קיימים הנתונים //קורא למתודות שמבצעות את הסריליזציה על התיקייה שנבחרה ומציב את הערך שמתקבל בליסטים הקיימים בתכנית AddTorem.donors= DeserilizationDonor(folders); AddTorem.donations = DeserilizationDonation(folders); AddTorem.credits = DeserilizationCreditCard(folders); AddTorem.accounts = DeserilizationBankAccount(folders); } } private static List<BankAccount> DeserilizationBankAccount(string folders) { string fileBank = folders; fileBank += @"\bankAccountList";//בוחר בקובץ הדרוש var myObject = new List<BankAccount>(); using (FileStream fs = new FileStream(fileBank, FileMode.Open)) { XmlSerializer xmlser = new XmlSerializer(typeof(List<BankAccount>)); myObject = (List<BankAccount>)xmlser.Deserialize(fs); return myObject; } } private static List<CreditCard> DeserilizationCreditCard(string folders) { string fileCredit = folders; fileCredit += @"\creditCardList"; var myObject = new List<CreditCard>(); using (FileStream fs = new FileStream(fileCredit, FileMode.Open)) { XmlSerializer xmlser = new XmlSerializer(typeof(List<CreditCard>)); myObject = (List<CreditCard>)xmlser.Deserialize(fs); return myObject; } } private static List<Donation> DeserilizationDonation(string folders) { string fileDonation = folders; fileDonation += @"\donationList"; var myObject = new List<Donation>(); using (FileStream fs = new FileStream(fileDonation, FileMode.Open)) { XmlSerializer xmlser = new XmlSerializer(typeof(List<Donation>)); myObject = (List<Donation>)xmlser.Deserialize(fs); return myObject; } } private static List<Donor> DeserilizationDonor(string folders) { string fileDonor = folders; fileDonor += @"\donorList"; var myObject = new List<Donor>(); using (FileStream fs = new FileStream(fileDonor, FileMode.Open)) { XmlSerializer xmlser = new XmlSerializer(typeof(List<Donor>)); myObject = (List<Donor>)xmlser.Deserialize(fs); return myObject; } } }
הכל עובד חלק!!! תודה רבה על העזרה והתמיכה!!
השאלה שלי היא האם אי אפשר לייעל את זה, כיון שבסוף נאלצתי לכתוב מתודה נפרדת לפתיחת כל ליסט. האם אי אפשר לכתוב מתודה גנרית שתדע לזהות לבד את הערך של כל ליסט הנשלח אליה ולחלץ אותו?
בטח זה אפשרי, אבל לא הצלחתי.. ניסיתי לכתוב במתודה List<T> אבל זה לא הצליח לי.. אז נאלצתי לכתוב 4 מתודות שבעצם עושות אותו דבר..פורסם במקור בפורום CODE613 ב30/07/2015 15:50 (+03:00)
-
א. מחיאות כפיים.
ב. בכל דסראילציה כתבת var myObject = new ...
מיותר בדסריאלזציה כיון שהדסיאליזר יוצר בעצמו אבייקט (אתה זוכר את זה http://tchumim.com/post/4509).
ג. אכן בדיוק כמו שניחשת ע"י מתודות גנריות אפשר בקלות לעשות פונקציה מרוכזת הן לסריאליזציה ון לדסריאליזציה, תראה קוד:public static class SerilizationAll { const string fileDonor = @"\donorList"; const string fileDonation = @"\donationList"; const string fileCredit = @"\creditCardList"; const string fileBank = @"\bankAccountList"; //סריליזציה של כל התכנית public static void Serilization() { FolderBrowserDialog folderBrowser = new FolderBrowserDialog(); if (folderBrowser.ShowDialog() == DialogResult.OK) { string folder = folderBrowser.SelectedPath; folder += @"\userData\data";//מוסיף לנתיב תיקיית משנה לשמירת הנתונים DirectoryInfo directoryInfo = new DirectoryInfo(folder); directoryInfo.Create();//יוצר את ספריות המשנה Serialize(AddTorem.donations, folder + fileDonation); Serialize(AddTorem.donors, folder + fileDonor); Serialize(AddTorem.credits, folder + fileCredit); Serialize(AddTorem.accounts, folder + fileBank); } } //deserilization for all program public static void Deserilization() { FolderBrowserDialog folderBrowser = new FolderBrowserDialog(); if (folderBrowser.ShowDialog() == DialogResult.OK) { string folders = folderBrowser.SelectedPath; folders += @"\userData\data";//מוסיף לנתיב את הנתיב לתיקיית משנה בה קיימים הנתונים //קורא למתודות שמבצעות את הסריליזציה על התיקייה שנבחרה ומציב את הערך שמתקבל בליסטים הקיימים בתכנית Deserilize(AddTorem.donors, folders + fileDonor); Deserilize(AddTorem.donations, folders + fileDonation); Deserilize(AddTorem.credits, folders + fileCredit); Deserilize(AddTorem.accounts, folders + fileBank); } } private static void Serialize<T>(T obj, string path) { using (FileStream bankStream = new FileStream(path, FileMode.Create)) { XmlSerializer xml = new XmlSerializer(typeof(T)); xml.Serialize(bankStream, obj); } } private static void Deserilize<T>(T returnObj, string path) { using (FileStream fs = new FileStream(path, FileMode.Open)) { XmlSerializer xmlser = new XmlSerializer(typeof(T)); returnObj = (T)xmlser.Deserialize(fs); } } }
בהצלחה רבה!
פורסם במקור בפורום CODE613 ב30/07/2015 16:34 (+03:00)
-
אגב כיון שהתעצלתי ליצור מחלקות כשלך בשביל לקבל תמיכה של השלמה אוטומטית ובדיקת שגיאות, אז בחרתי בדרך של מתודה גנרית לDeserilize, אף שבכל מקרה הוא מחזיר Object וצריך לעשות לו cast, ממילא לך יותר הגיוני לשנות אותו לככה:
private static object Deserilize(string path) { using (FileStream fs = new FileStream(path, FileMode.Open)) { XmlSerializer xmlser = new XmlSerializer(typeof(T)); return xmlser.Deserialize(fs); } }
ובמתודה הקוראת לשנות את זה:
Deserilize(AddTorem.accounts, folders + fileBank);
לזה:
AddTorem.accounts = (Account)Deserilize(folders + fileBank);
עוד הערה, אתה יוצר תיקיה ללא בדיקה מוקדמת - ייתכן שהיא קיימת כבר.
פורסם במקור בפורום CODE613 ב30/07/2015 16:44 (+03:00)
-
בשביל לקצר את הקוד והמורכבות הייתי עושה כך
יוצר מחלקה אבסטרקטית היורשת מליסט או ObservableCollection
מחלקה זו תטפל בכל הנושא של קריאה וכתיבה מהדיסק
וכל הליסטים האחרים של התרומות ותורמים וכו' ירשו ממחלקה אבסטרקטית הנ"ל וכך כל דבר יהיה כתוב רק פעם אחת.
כשתרצה להוסיף ליסט חדש לתוכנה כל שעליך לעשות זה להצהיר שהוא יורש מהמחלקה הנ"ל ולהמציא נתיב שמירה.פורסם במקור בפורום CODE613 ב30/07/2015 17:09 (+03:00)
-
@דוד ל.ט.
תוכל גם במקום להקצות שם לקובץ אחד אחד להשתמש בשם הקלאס ע"י GetType().Name
תיזהר פעם הבאה שאתה מבקש עצות, סכנת הצפה
מעולה!! עצות מחכימות ומועילות!! הלוואי וכל הסכנות היו כאלה :lol:
שיניתי את כל המחלקה כמו שאמרת!!
לגבי העצה לעשות את ההמרה במתודה של הדיסריליזציה לא במתודה עצמה: זה לא עבד לי אלא נתן הודעת שגיאה שאת ההמרה צריך לבצע מיד במקום.בשביל לקצר את הקוד והמורכבות הייתי עושה כך
יוצר מחלקה אבסטרקטית היורשת מליסט או ObservableCollection
מחלקה זו תטפל בכל הנושא של קריאה וכתיבה מהדיסק
וכל הליסטים האחרים של התרומות ותורמים וכו' ירשו ממחלקה אבסטרקטית הנ"ל וכך כל דבר יהיה כתוב רק פעם אחת.
כשתרצה להוסיף ליסט חדש לתוכנה כל שעליך לעשות זה להצהיר שהוא יורש מהמחלקה הנ"ל ולהמציא נתיב שמירה.הבעיה עם זה שאם יש לי מחלקות שיורשות ממחלקות אחרות בתכנית זה לא יועיל, כי אפשר לרשת רק ממחלקה אחת.
תודה רבה רבה על כל העצות!! מה הייתי עושה בלעדיכם :lol:
פורסם במקור בפורום CODE613 ב31/07/2015 08:26 (+03:00)
-
עוד הערה לגבי המתודה של הדיסרילזציה חייבים להציב אותה בערכים המתאימים, ולא מספיק לקרוא לה כמו שכתבת כי הוא כנראה עדיין לא יודע לאן להחזיר את הערך שמתקבל.
ככהAddTorem.donors = Deserilization(AddTorem.donors, folders + fileDonor); AddTorem.donations = Deserilization(AddTorem.donations, folders + fileDonation); AddTorem.accounts = Deserilization(AddTorem.accounts, folders + fileBank); AddTorem.credits = Deserilization(AddTorem.credits, folders + fileCredit);
פורסם במקור בפורום CODE613 ב31/07/2015 09:09 (+03:00)
-
הבעיה עם זה שאם יש לי מחלקות שיורשות ממחלקות אחרות בתכנית זה לא יועיל, כי אפשר לרשת רק ממחלקה אחת.
ספר יותר מה ההיררכיה של המחלקות אצלך בתוכנה, כי בעיקרון כל המחלקות שמחזיקות את הנתונים ומטפלות בנתונים יכולות לרשת אחד מהשני כמה דורות. אולי תביא דיאגרמה.
פורסם במקור בפורום CODE613 ב31/07/2015 11:18 (+03:00)
-
רחמים לא התכוון במחלקות שלך, אלא במקום מחלקת הList תירש הימנה (או מObservablECollection שזה ליסט שנותן נוטיפקציה לשינויים שזה מאוד שימושי בWPF).
@avr416לגבי העצה לעשות את ההמרה במתודה של הדיסריליזציה לא במתודה עצמה: זה לא עבד לי אלא נתן הודעת שגיאה שאת ההמרה צריך לבצע מיד במקום.
אני לא רואה למה.
אם זה לא קשה (זה כן) אז תגיד לי בדיוק איפה השגיאה ומה והאם היא בזמן כתיבה - קומפילציה, או זמן ריצה.פורסם במקור בפורום CODE613 ב31/07/2015 11:39 (+03:00)
-
@דוד ל.ט.
אם זה לא קשה (זה כן) אז תגיד לי בדיוק איפה השגיאה ומה והאם היא בזמן כתיבה - קומפילציה, או זמן ריצה.
דווקא לא קשה כי השגיאה בזמן כתיבה.
הודעת השגיאה היא:
(?Cannot implicitly convert type 'object' to 'T'. An explicit conversion exists (are you missing a castפורסם במקור בפורום CODE613 ב31/07/2015 16:21 (+03:00)
-
בשביל לקצר את הקוד והמורכבות הייתי עושה כך
יוצר מחלקה אבסטרקטית היורשת מליסט או ObservableCollection
מחלקה זו תטפל בכל הנושא של קריאה וכתיבה מהדיסק
וכל הליסטים האחרים של התרומות ותורמים וכו' ירשו ממחלקה אבסטרקטית הנ"ל וכך כל דבר יהיה כתוב רק פעם אחת.
כשתרצה להוסיף ליסט חדש לתוכנה כל שעליך לעשות זה להצהיר שהוא יורש מהמחלקה הנ"ל ולהמציא נתיב שמירה.עכשיו אני רואה שלא הבנתי את דבריך
אתה מתכוון שאצור מח' שמטפלת בשמירה ופתיחה והיא תירש ממח' הליסט ודומיה, ואח"כ המח' הראשונה בתכנית תירש ממנה, ונמצא שכל המח' שיורשות מאותה מח' יוכלו לממש את הפונקציות שנמצאות במח' הטיפול בקבצים.
אבל למה זה מקצר את הקוד והמורכבות לא כ"כ הבנתי..
תודה רבה ושבת שלום!!פורסם במקור בפורום CODE613 ב31/07/2015 16:39 (+03:00)
-
ונמצא שכל המח' שיורשות מאותה מח' יוכלו לממש את הפונקציות שנמצאות במח' הטיפול בקבצים.
לא, הם לא צריכים לממש המימוש כבר קיים במחלקה האבסטרקטית בשביל זה אמרתי מחלקה ולא אינטרפייס
כשתרצה להוסיף ליסט חדש לתוכנה כל שעליך לעשות זה להצהיר שהוא יורש מהמחלקה הנ"ל ולהמציא נתיב שמירה [ודוד פטר אותך גם מזה, אמנם אם תרצה לעשות ערפול לקוד שלך לא תוכל להסתמך על Type.Name.]
וזה החיסכון האדיר כאן בקוד וגם יש כאן סדר וכל מחלקה מתעסקת במה שהיא מיועדת בלבד, אין מלאך עושה שתי שליחויות וכל שכן מחלקהפורסם במקור בפורום CODE613 ב31/07/2015 17:22 (+03:00)
-
@דוד ל.ט.
תוכל גם במקום להקצות שם לקובץ אחד אחד להשתמש בשם הקלאס ע"י GetType().Name
ניסיתי וזה לא עובד..
התכנה רצה והכל לכאו' מצויין, אני שומר את הקובץ ואח"כ מנסה לפתוח אותו ואז מקבל הודעת שגיאה שהקובץ אינו תקין.
פתחתי את התיקייה ואני רואה שהוא לא יצר שום קובץ.. רק את התיקיות משנה..
ניסיתי עוד כמה פעמים ושוב פעם אותו דבר..
זה המתודה של השמירה..public static void Serialize<T>(T obj) { using (FileStream fs = new FileStream(obj.GetType().Name, FileMode.Create)) { XmlSerializer donorXmlS = new XmlSerializer(typeof(T)); donorXmlS.Serialize(fs, obj); } }
אני מצרף כאן את כל הקבצים של התכנה.. אם זה יעזור למישהו..
AppDonorNew.rar
שכוייח!!פורסם במקור בפורום CODE613 ב02/08/2015 00:08 (+03:00)
-
פשוט מאוד.
אתה צריך ללמוד קצת לדבאג. אם תעקוב אחרי מה שקורה, אז דבר ראשון תראה ששם שחוזר מהמתודה GetType().Name הוא List'1. למזלך זה שם חוקי, אבל הבעיה שהשם זהה עבור כל הליסטים! בקריאה יש שגיאה כי יש בXML סוג שונה לחלוטין (הסוג האחרון שנשמר הוא הדורס האחרון)...
בשביל לפתור זאת צריך לגשת לטיפוס הפרמטר הגנרי (הT של הליסט). חיפשתי בגוגל ומצאתי, ככה:GetType().GenericTypeArguments[0].Name
דבר שני, שים לב שאתה מעביר שם קובץ ללא נתיב. ומתודת הסריאליזציה והדסריאליזציה לא מקבלות שום פרמטר בנוגע לתיקיה בה לטעון ולשמור. אז למה אין שגיאה? כי אז זה נשמר יחסית ל"תיקיה נוכחית" ש[u:4yywltfk]בדרך כלל[/u:4yywltfk] זו התיקיה ממנה הרצת את התוכנה.
הצלחה רבה!
על כל שגיאה בתכנות יש מאה הצלחות בעתיד.פורסם במקור בפורום CODE613 ב02/08/2015 12:59 (+03:00)
-
הנה הקוד המלא של המחלקה אחרי כל התיקונים:
public static class SerilizationAll { //סריליזציה של כל התכנית public static void Serilization() { FolderBrowserDialog folderBrowser = new FolderBrowserDialog(); if (folderBrowser.ShowDialog() == DialogResult.OK) { string folder = folderBrowser.SelectedPath; folder += @"\userData\data\";//מוסיף לנתיב תיקיית משנה לשמירת הנתונים DirectoryInfo directoryInfo = new DirectoryInfo(folder); directoryInfo.Create();//יוצר את ספריות המשנה //קורא למתודה שמבצעת את הסריליזציה Serialize(AddTorem.donations,folder+AddTorem.donations.GetType().GenericTypeArguments[0].Name); Serialize(AddTorem.donors,folder+AddTorem.donors.GetType().GenericTypeArguments[0].Name); Serialize(AddTorem.credits,folder+AddTorem.credits.GetType().GenericTypeArguments[0].Name); Serialize(AddTorem.accounts,folder+AddTorem.accounts.GetType().GenericTypeArguments[0].Name); } } // מתודה גנרית לסריליזציה שמקבלת את סוג האובייקט והנתיב לשמירה private static void Serialize<T>(T obj,string path) { using (FileStream fs = new FileStream(path, FileMode.Create)) { XmlSerializer donorXmlS = new XmlSerializer(typeof(T)); donorXmlS.Serialize(fs, obj); } } //deserilization for all program public static void Deserilization() { FolderBrowserDialog folderBrowser = new FolderBrowserDialog(); if (folderBrowser.ShowDialog() == DialogResult.OK) { string folders = folderBrowser.SelectedPath; folders += @"\userData\data\";//מוסיף לנתיב את הנתיב לתיקיית משנה בה קיימים הנתונים //קורא למתודות שמבצעות את הסריליזציה על התיקייה שנבחרה AddTorem.donors = Deserilization(AddTorem.donors, folders + AddTorem.donors.GetType().GenericTypeArguments[0].Name); AddTorem.donations = Deserilization(AddTorem.donations, folders + AddTorem.donations.GetType().GenericTypeArguments[0].Name); AddTorem.accounts = Deserilization(AddTorem.accounts, folders + AddTorem.accounts.GetType().GenericTypeArguments[0].Name); AddTorem.credits = Deserilization(AddTorem.credits,folders+AddTorem.credits.GetType().GenericTypeArguments[0].Name); } } //מתודה גנרית לדיסריליזציה שמקבלת את סוג האובייקט והנתיב לפתיחה private static T Deserilization<T>(T returnObj,string path) { using (FileStream fs = new FileStream(path, FileMode.Open)) { XmlSerializer xmlser = new XmlSerializer(typeof(T)); return returnObj= (T)xmlser.Deserialize(fs); } } } }
הקוד עושה אותו דבר, יותר יעיל והרבה הרבה יותר קצר!!
תודה לכולם על כל העצות!!! אין על הפורום שלנו :lol: :lol:פורסם במקור בפורום CODE613 ב02/08/2015 16:32 (+03:00)