2015-10-30 5 views
10

का उपयोग कर एक्सेल पंक्तियों को वापस टाइप करने के लिए कैसे करें ईपीप्लस में वर्कशीट में अपने स्वयं के प्रकार के डेटा प्राप्त करने के लिए सुविधाजनक LoadFromCollection<T> विधि है।ईपीप्लस

उदाहरण के लिए अगर मैं एक वर्ग है:

public class Customer 
{ 
    public int Id { get; set; } 
    public string Firstname { get; set; } 
    public string Surname { get; set; } 
    public DateTime Birthdate { get; set; } 
} 

तो निम्न कोड:

var package = new ExcelPackage(); 
var sheet = package.Workbook.Worksheets.Add("Customers"); 
var customers = new List<Customer>{ 
    new Customer{ 
     Id = 1, 
     Firstname = "John", 
     Surname = "Doe", 
     Birthdate = new DateTime(2000, 1, 1) 
    }, 
    new Customer{ 
     Id = 2, 
     Firstname = "Mary", 
     Surname = "Moe", 
     Birthdate = new DateTime(2001, 2, 2) 
    } 
}; 
sheet.Cells[1, 1].LoadFromCollection(customers); 
package.Save(); 

... किसी कार्यपत्रक "ग्राहकों" कहा जाता है के लिए 2 पंक्तियाँ जोड़ देगा।

मेरा सवाल यह है कि अगर एक्सेल से पंक्तियों को निकालने के लिए एक सुविधाजनक समकक्ष है (उदाहरण के लिए कुछ संशोधनों के बाद) मेरे प्रकार में वापस आ गया है।

कुछ की तरह:

var package = new ExcelPackage(inputStream); 
var customers = sheet.Dimension.SaveToCollection<Customer>() ?? 

मैं

  • EPPlus codebase
  • किसी भी saving सवाल
  • किसी भी parsing प्रश्नों के लिए खोज के लिए खोज के माध्यम से तलाश कर दिया गया
  • देखा this सवाल है रीडिन पर जी एकल कोशिकाओं

... लेकिन मेरे प्रकार के लिए पंक्तियों को आसानी से कैसे पार्स करने के बारे में कुछ भी नहीं मिला।

उत्तर

18

उपर्युक्त से प्रेरित मैंने इसे थोड़ा अलग रास्ता लिया।

  1. मैंने एक विशेषता बनाई और प्रत्येक संपत्ति को कॉलम में मैप किया।
  2. मैं डीटीओ प्रकार का उपयोग मैं क्या प्रत्येक स्तंभ होने के लिए
  3. अनुमति दें स्तंभ प्रकार

कन्वर्ट करने के लिए ऐसा करके requried नहीं किया जा करने के लिए

  • उपयोग EPPlus यह मेरे पारंपरिक का उपयोग करने की अनुमति देता है उम्मीद परिभाषित करने के लिए

    using(FileStream fileStream = new FileStream(_fileName, FileMode.Open)){ 
         ExcelPackage excel = new ExcelPackage(fileStream); 
         var workSheet = excel.Workbook.Worksheets[RESOURCES_WORKSHEET]; 
    
         IEnumerable<ExcelResourceDto> newcollection = workSheet.ConvertSheetToObjects<ExcelResourceDto>(); 
         newcollection.ToList().ForEach(x => Console.WriteLine(x.Title)); 
    } 
    
    : उपयोग - मॉडल सत्यापन करें और कॉलम हेडर्स

    में परिवर्तन को गले

    Dto

    public class ExcelResourceDto 
    { 
        [Column(1)] 
        [Required] 
        public string Title { get; set; } 
    
        [Column(2)] 
        [Required] 
        public string SearchTags { get; set; } 
    } 
    

    उत्कृष्टता प्राप्त करने यह विशेषता परिभाषा

    [AttributeUsage(AttributeTargets.All)] 
    public class Column : System.Attribute 
    { 
        public int ColumnIndex { get; set; } 
    
    
        public Column(int column) 
        { 
         ColumnIndex = column; 
        } 
    } 
    

    एक्सटेंशन वर्ग डीटीओ

    public static class EPPLusExtensions 
    { 
        public static IEnumerable<T> ConvertSheetToObjects<T>(this ExcelWorksheet worksheet) where T : new() 
        { 
    
         Func<CustomAttributeData, bool> columnOnly = y => y.AttributeType == typeof(Column); 
    
         var columns = typeof(T) 
           .GetProperties() 
           .Where(x => x.CustomAttributes.Any(columnOnly)) 
         .Select(p => new 
         { 
          Property = p, 
          Column = p.GetCustomAttributes<Column>().First().ColumnIndex //safe because if where above 
         }).ToList(); 
    
    
         var rows= worksheet.Cells 
          .Select(cell => cell.Start.Row) 
          .Distinct() 
          .OrderBy(x=>x); 
    
    
         //Create the collection container 
         var collection = rows.Skip(1) 
          .Select(row => 
          { 
           var tnew = new T(); 
           columns.ForEach(col => 
           { 
            //This is the real wrinkle to using reflection - Excel stores all numbers as double including int 
            var val = worksheet.Cells[row, col.Column]; 
            //If it is numeric it is a double since that is how excel stores all numbers 
            if (val.Value == null) 
            { 
             col.Property.SetValue(tnew, null); 
             return; 
            } 
            if (col.Property.PropertyType == typeof(Int32)) 
            { 
             col.Property.SetValue(tnew, val.GetValue<int>()); 
             return; 
            } 
            if (col.Property.PropertyType == typeof(double)) 
            { 
             col.Property.SetValue(tnew, val.GetValue<double>()); 
             return; 
            } 
            if (col.Property.PropertyType == typeof(DateTime)) 
            { 
             col.Property.SetValue(tnew, val.GetValue<DateTime>()); 
             return; 
            } 
            //Its a string 
            col.Property.SetValue(tnew, val.GetValue<string>()); 
           }); 
    
           return tnew; 
          }); 
    
    
         //Send it back 
         return collection; 
        } 
    } 
    
  • +2

    महान काम करता है। दूसरों को ध्यान दें: 'const int RESOURCES_WORKSHEET = 1' (वर्कशीट इंडेक्स 1 आधारित हैं) – fiat

    +2

    यह संशोधित करने के लिए सीधा है इसलिए कॉलम विशेषता कॉलम इंडेक्स के बजाय कॉलम नाम निर्दिष्ट करती है। – subsci

    +0

    मैं प्रॉपर्टीइन्फो और कॉलम को एक सूची में मैप करने का एक प्रभावी तरीका खोज रहा था, 'फनक' का अच्छा उपयोग और लिंक का अच्छा उपयोग, मैंने उसी ऑपरेशन के लिए 7.5% में उपभोग करने में समय कम किया है जो ऐसा नहीं लगता है बहुत, लेकिन यह 680k एमएस से 50k एमएस पर जाता है यदि आप समानांतर लागू करते हैं तो यह समाधान तेज़ होगा। मैंने इस उत्तर से बहुत कुछ सीख लिया है। धन्यवाद! – Nekeniehl

    4

    दुर्भाग्य से ईपीप्लस के मूल निवासी ऐसी कोई विधि नहीं है। यह क्रैक करने के लिए एक कठिन अखरोट है क्योंकि आपको प्रतिबिंब का उपयोग करना होगा यदि आप वास्तव में इसे सामान्य बनाना चाहते हैं। और एक्सेल की वजह से सभी संख्याओं और तिथियों को डबल के रूप में संग्रहीत करने के कारण आपको बहुत सारे अनबॉक्सिंग से निपटना होगा और चेक टाइप करना होगा।

    यह कुछ है जो मैं काम कर रहा हूं। इसकी एक विस्तार विधि है जो इसे Generics के माध्यम से करेगी। यह काम करता है लेकिन सीमित परीक्षण के तहत ही सुनिश्चित करें कि आप इसे स्वयं जांचें। मैं गारंटी नहीं दे सकता कि यह सबसे अनुकूलित (अभी तक) है लेकिन यह उसके बिंदु पर बहुत सभ्य है। आप इसे इस तरह का प्रयोग करेंगे:

    IEnumerable<TestObject> newcollection = worksheet.ConvertSheetToObjects<TestObject>(); 
    

    विस्तार:

    public static IEnumerable<T> ConvertSheetToObjects<T>(this ExcelWorksheet worksheet) where T:new() 
    { 
        //DateTime Conversion 
        var convertDateTime = new Func<double, DateTime>(excelDate => 
        { 
         if (excelDate < 1) 
          throw new ArgumentException("Excel dates cannot be smaller than 0."); 
    
         var dateOfReference = new DateTime(1900, 1, 1); 
    
         if (excelDate > 60d) 
          excelDate = excelDate - 2; 
         else 
          excelDate = excelDate - 1; 
         return dateOfReference.AddDays(excelDate); 
        }); 
    
        //Get the properties of T 
        var tprops = (new T()) 
         .GetType() 
         .GetProperties() 
         .ToList(); 
    
        //Cells only contains references to cells with actual data 
        var groups = worksheet.Cells 
         .GroupBy(cell => cell.Start.Row) 
         .ToList(); 
    
        //Assume the second row represents column data types (big assumption!) 
        var types = groups 
         .Skip(1) 
         .First() 
         .Select(rcell => rcell.Value.GetType()) 
         .ToList(); 
    
        //Assume first row has the column names 
        var colnames = groups 
         .First() 
         .Select((hcell, idx) => new { Name = hcell.Value.ToString(), index = idx }) 
         .Where(o => tprops.Select(p => p.Name).Contains(o.Name)) 
         .ToList(); 
    
        //Everything after the header is data 
        var rowvalues = groups 
         .Skip(1) //Exclude header 
         .Select(cg => cg.Select(c => c.Value).ToList()); 
    
    
        //Create the collection container 
        var collection = rowvalues 
         .Select(row => 
         { 
          var tnew = new T(); 
          colnames.ForEach(colname => 
          { 
           //This is the real wrinkle to using reflection - Excel stores all numbers as double including int 
           var val = row[colname.index]; 
           var type = types[colname.index]; 
           var prop = tprops.First(p => p.Name == colname.Name); 
    
           //If it is numeric it is a double since that is how excel stores all numbers 
           if (type == typeof (double)) 
           { 
            //Unbox it 
            var unboxedVal = (double) val; 
    
            //FAR FROM A COMPLETE LIST!!! 
            if (prop.PropertyType == typeof (Int32)) 
             prop.SetValue(tnew, (int) unboxedVal); 
            else if (prop.PropertyType == typeof (double)) 
             prop.SetValue(tnew, unboxedVal); 
            else if (prop.PropertyType == typeof (DateTime)) 
             prop.SetValue(tnew, convertDateTime(unboxedVal)); 
            else 
             throw new NotImplementedException(String.Format("Type '{0}' not implemented yet!", prop.PropertyType.Name)); 
           } 
           else 
           { 
            //Its a string 
            prop.SetValue(tnew, val); 
           } 
          }); 
    
          return tnew; 
         }); 
    
    
        //Send it back 
        return collection; 
    } 
    

    इसके पूर्ण उदाहरण:

    [TestMethod] 
    public void Read_To_Collection_Test() 
    { 
        //A collection to Test 
        var objectcollection = new List<TestObject>(); 
    
        for (var i = 0; i < 10; i++) 
         objectcollection.Add(new TestObject {Col1 = i, Col2 = i*10, Col3 = Path.GetRandomFileName(), Col4 = DateTime.Now.AddDays(i)}); 
    
        //Create a test file to convert back 
        byte[] bytes; 
        using (var pck = new ExcelPackage()) 
        { 
         //Load the random data 
         var workbook = pck.Workbook; 
         var worksheet = workbook.Worksheets.Add("data"); 
         worksheet.Cells.LoadFromCollection(objectcollection, true); 
         bytes = pck.GetAsByteArray(); 
        } 
    
    
        //********************************* 
        //Convert from excel to a collection 
        using (var pck = new ExcelPackage(new MemoryStream(bytes))) 
        { 
         var workbook = pck.Workbook; 
         var worksheet = workbook.Worksheets["data"]; 
    
         var newcollection = worksheet.ConvertSheetToObjects<TestObject>(); 
         newcollection.ToList().ForEach(to => Console.WriteLine("{{ Col1:{0}, Col2: {1}, Col3: \"{2}\", Col4: {3} }}", to.Col1, to.Col2, to.Col3, to.Col4.ToShortDateString())); 
        } 
    } 
    
    //test object class 
    public class TestObject 
    { 
        public int Col1 { get; set; } 
        public int Col2 { get; set; } 
        public string Col3 { get; set; } 
        public DateTime Col4 { get; set; } 
    } 
    

    कंसोल आउटपुट:

    { Col1:0, Col2: 0, Col3: "wrulvxbx.wdv", Col4: 10/30/2015 } 
    { Col1:1, Col2: 10, Col3: "wflh34yu.0pu", Col4: 10/31/2015 } 
    { Col1:2, Col2: 20, Col3: "ps0f1jg0.121", Col4: 11/1/2015 } 
    { Col1:3, Col2: 30, Col3: "skoc2gx1.2xs", Col4: 11/2/2015 } 
    { Col1:4, Col2: 40, Col3: "urs3jnbb.ob1", Col4: 11/3/2015 } 
    { Col1:5, Col2: 50, Col3: "m4l2fese.4yz", Col4: 11/4/2015 } 
    { Col1:6, Col2: 60, Col3: "v3dselpn.rqq", Col4: 11/5/2015 } 
    { Col1:7, Col2: 70, Col3: "v2ggbaar.r31", Col4: 11/6/2015 } 
    { Col1:8, Col2: 80, Col3: "da4vd35p.msl", Col4: 11/7/2015 } 
    { Col1:9, Col2: 90, Col3: "v5dtpuad.2ao", Col4: 11/8/2015 } 
    
    +0

    बहुत बढ़िया @Ernie, साझा करने के लिए धन्यवाद करने के लिए मानचित्रण पंक्तियों को संभालने के लिए है नक्शे कि। वास्तव में, कुछ धारणाएं हैं, लेकिन इसे हल करने के तरीके में एक महान अंतर्दृष्टि प्रदान करती है। –

    संबंधित मुद्दे