2011-08-15 11 views
10

Khi tôi hack qua cơ sở mã của chúng tôi, tôi chỉ nhận thấy chức năng này. Nó chuyển đổi một IDictionary<string, object> (Paramters - một biến mẫu) thành một chuỗi XML.Thách thức: Bạn có thể làm cho chức năng đơn giản này thanh lịch hơn bằng cách sử dụng C# 4.0

Điều này chẳng là gì ngoài sự tò mò về phần của tôi :-).

Vì vậy, nó có thể được viết với mã ít hơn rất nhiều bằng cách sử dụng C# 4.0? Quy tắc: không có libs bên ngoài ngoại trừ .Net Framework BCL.

Để làm cho nó trở nên khó khăn hơn, tôi không đặt thông số từ điển đầu vào ở đây, vì bạn có thể làm việc từ mã.

public string ConvertToXml() { 
    XmlDocument doc = new XmlDocument(); 
    doc.LoadXml("<?xml version='1.0' encoding='utf-8'?><sc/>"); 
    foreach (KeyValuePair<string, object> param in Parameters) { 
     XmlElement elm = doc.CreateElement("pr"); 

     if (param.Value is int || param.Value is Int32 || param.Value is Int16 || param.Value is Int64) { 
      elm.SetAttribute("tp", "int"); 
     } else if (param.Value is DateTime?){ 
      elm.SetAttribute("tp", "datetime"); 
     } else { 
      elm.SetAttribute("tp", "string"); 
     } 

     elm.SetAttribute("nm", param.Key); 
     if (param.Value is DateTime?) { 
      DateTime? dateTime = param.Value as DateTime?; 
      elm.SetAttribute("vl", dateTime.Value.ToString("o")); 
     } else{ 
      elm.SetAttribute("vl", param.Value.ToString()); 
     } 
     doc.FirstChild.NextSibling.AppendChild(elm); 
    } 
    return doc.OuterXml; 
} 

Hãy để tôi thêm một số suy nghĩ khác.

Đối với tôi:

  • ít hơn nhưng ngắn gọn là xấu hơn
  • loại cũng tốt, nhưng các loại tầm thường dường như có mùi
  • tái sử dụng là tốt
+1

Nếu đây là một thách thức bạn nên cung cấp một số tiền thưởng :) – aevanko

+0

Tôi sẽ làm. Tôi không biết cách thêm tiền thưởng ngay từ đầu - tôi nghĩ bạn phải đợi. Tôi làm gì? –

+0

Ah bạn nói đúng, bạn cần chờ 2 ngày. : 0 xấu của tôi.Nhìn về phía trước để xem các giải pháp mọi người đến với^^ – aevanko

Trả lời

6

Sử dụng năng động và LINQ to XML:

ConvertToXml có thể được giảm xuống một tuyên bố (giả sử bỏ qua khai báo XML là chấp nhận được).

public string ConvertToXml() 
{ 
    return new XElement("sc", 
     Parameters.Select(param => CreateElement(param.Key, (dynamic)param.Value)) 
    ).ToString(SaveOptions.DisableFormatting); 
} 

Lưu ý rằng CreateElement phôi param.Value động để quá tải chính xác từ sau sẽ được chọn khi chạy.

XElement CreateElement(string key, object value) 
{ 
    return CreateElement("string", key, value.ToString()); 
} 

XElement CreateElement(string key, long value) 
{ 
    return CreateElement("int", key, value.ToString()); 
} 

XElement CreateElement(string key, DateTime value) 
{ 
    return CreateElement("datetime", key, value.ToString("o")); 
} 

Các quá tải trên rốt cuộc gọi:

XElement CreateElement(string typename, string key, string value) 
{ 
    return new XElement("pr", 
     new XAttribute("tp", typename), 
     new XAttribute("nm", key), 
     new XAttribute("vl", value) 
    ); 
} 

Mã này là giảm số lượng các báo cáo (mặc dù không lines) tìm thấy trong câu hỏi. Cách tiếp cận này xây dựng trên svick's, nhưng giảm số lượng các phương thức và cuộc gọi động được yêu cầu.

+1

Trong số các giải pháp sử dụng 'dynamic', tôi thích mẫu này nhất. –

+1

Cảm ơn bạn. Tôi thích cái này nhất. –

2

Reapproached xem xét yêu cầu mới.

  • chi tiết chuyển đổi tách riêng của từng loại và thế hệ XML Logic bê tông tự
  • một cách dễ dàng có thể được giới thiệu mới kiểu dữ liệu hỗ trợ bằng cách thêm một nhà máy mới để các nhà cung cấp. Tập hợp loại hiện được hỗ trợ bị giới hạn bởi các thành viên điều tra TypeCode nhưng rõ ràng điều này có thể dễ dàng chuyển sang bộ chọn/số nhận dạng khác.
  • Tôi phải đồng ý với jbtule rằng Tuple .Tạo() thực sự trông đẹp hơn nhiều thay vì sau đó xây dựng KeyValuePair<,>, không bao giờ được sử dụng trước đó, nội dung thú vị, cảm ơn!

Phương pháp riêng của mình:

public string ConvertToXml(
    IDictionary<string, object> rawData, 
     Dictionary<TypeCode, Func<object, Tuple<string, string>>> transformationFactoryProvider) 
{ 
    XmlDocument doc = new XmlDocument(); 
    doc.LoadXml("<?xml version='1.0' encoding='utf-8'?><sc/>"); 

    if (rawData != null) 
    { 
     Func<object, Tuple<string, string>> defaultFactory = 
       (raw) => Tuple.Create("string", raw.ToString()); 

     foreach (KeyValuePair<string, object> item in rawData) 
     { 
      TypeCode parameterTypeCode = Type.GetTypeCode(item.Value.GetType()); 
      var transformationFactory = transformationFactoryProvider.ContainsKey(parameterTypeCode) 
              ? transformationFactoryProvider[parameterTypeCode] 
              : defaultFactory; 

      var transformedItem = transformationFactory(item.Value); 
      XmlElement xmlElement = doc.CreateElement("pr"); 
      xmlElement.SetAttribute("tp", transformedItem.Item1); 
      xmlElement.SetAttribute("nm", item.Key); 
      xmlElement.SetAttribute("vl", transformedItem.Item2); 
      doc.FirstChild.NextSibling.AppendChild(xmlElement); 
     } 
    } 

    return doc.OuterXml; 
} 

Làm thế nào để sử dụng ví dụ:

// Transformation Factories 
// Input: raw object 
// Output: Item1: type name, Item2: value in the finally formatted string 
Func<object, Tuple<string, string>> numericFactory = raw => Tuple.Create("int", raw.ToString()); 
Func<object, Tuple<string, string>> dateTimeFactory = 
    raw => Tuple.Create("datetime", (raw as DateTime?).GetValueOrDefault().ToString("o")); 

// Transformation Factory Provider 
// Input: TypeCode 
// Output: transformation factory for the given type 
var transformationFactoryProvider = 
    new Dictionary<TypeCode, Func<object, Tuple<string, string>>> 
     {       
      {TypeCode.Int16, numericFactory}, 
      {TypeCode.Int32, numericFactory}, 
      {TypeCode.Int64, numericFactory}, 
      {TypeCode.DateTime, dateTimeFactory} 
     }; 

// Convert to XML given parameters 
IDictionary<string, object> parameters = new Dictionary<string, object> 
         { 
           { "SOMEDATA", 12 }, 
           { "INTXX", 23 }, 
           { "DTTM", DateTime.Now }, 
           { "PLAINTEXT", "Plain Text" }, 
           { "RAWOBJECT", new object() }, 
          }; 
string xmlParameters = this.ConvertToXml(parameters, transformationFactoryProvider); 
+1

Oh thôi nào, KeyValuePairs của bạn sẽ trông đẹp hơn nhiều như Tuples. 'Tuple.Create' vs' KeyValuePair mới 'chỉ cần nói. – jbtule

+0

@jbtule: bạn đúng, điều gọn gàng! Cảm ơn! – sll

8

Sử dụng LINQ to XML có thể làm cho điều này rất đơn giản để viết lên. Thích điều này hơn các thư viện XML tiêu chuẩn nếu bạn có sự lựa chọn.

Tôi tin rằng đây sẽ tương đương:

public static string ToXmlString(this IDictionary<string, object> dict) 
{ 
    var doc = new XDocument(new XElement("sc", dict.Select(ToXElement))); 

    using (var writer = new Utf8StringWriter()) 
    { 
     doc.Save(writer); // "hack" to force include the declaration 
     return writer.ToString(); 
    } 
} 

class Utf8StringWriter : StringWriter 
{ 
    public override Encoding Encoding { get { return Encoding.UTF8; } } 
} 

static XElement ToXElement(KeyValuePair<string, object> kvp) 
{ 
    var value = kvp.Value ?? String.Empty; 

    string typeString; 
    string valueString; 
    switch (Type.GetTypeCode(value.GetType())) 
    { 
    case TypeCode.Int16: 
    case TypeCode.Int32: 
    case TypeCode.Int64: 
     typeString = "int"; 
     valueString = value.ToString(); 
     break; 
    case TypeCode.DateTime: 
     typeString = "datetime"; 
     valueString = ((DateTime)value).ToString("o"); 
     break; 
    default: 
     typeString = "string"; 
     valueString = value.ToString(); 
     break; 
    } 

    return new XElement("pr", 
     new XAttribute("tp", typeString), 
     new XAttribute("nm", kvp.Key), 
     new XAttribute("vl", valueString)); 
} 

Lưu ý rằng kiểm tra nếu giá trị là loại DateTime? là vô nghĩa. Tôi không chắc giá trị có trong việc lưu trữ các giá trị null trong từ điển nhưng nếu có thể, bạn sẽ mất thông tin loại đó bằng cách tạo giá trị kiểu object.

Ngoài ra, nếu có giá trị DateTime? không phải là null, thì bản thân giá trị sẽ được đóng hộp chứ không phải chính cấu trúc Nullable<DateTime>.Vì vậy, loại thực tế sẽ là DateTime đó là lý do tại sao mã này hoạt động.

5

Sử dụng các tính năng .net 4.0 chẳng hạn như Tupledynamic từ khóa. Các trường hợp thử nghiệm của tôi tạo ra kết quả chính xác của câu hỏi gốc.

//using System; 
//using System.Collections.Generic; 
//using System.Linq; 
//using System.Xml.Linq; 

    public string ConvertToXml() 
    { 
     //Create XDocument to specification with linq-to-xml 
     var doc = new XDocument(
      new XElement("sc", 
        from param in Parameters 
        //Uses dynamic invocation to use overload resolution at runtime 
        let attributeVals = AttributeValues((dynamic)param.Value) 
        select new XElement("pr", 
           new XAttribute("tp", attributeVals.Item1), 
           new XAttribute("nm", param.Key), 
           new XAttribute("vl", attributeVals.Item2) 
          ) 
      ) 
     ); 
     //Write to string 
     using (var writer = new Utf8StringWriter()) 
     { 
      doc.Save(writer, SaveOptions.DisableFormatting);//Don't add whitespace 
      return writer.ToString(); 
     } 
    } 
    //C# overloading will choose `long` as the best pick for `short` and `int` types too 
    static Tuple<string, string> AttributeValues(long value) 
    { 
     return Tuple.Create("int", value.ToString()); 
    } 
    //Overload for DateTime 
    static Tuple<string, string> AttributeValues(DateTime value) 
    { 
     return Tuple.Create("datetime", value.ToString("o")); 
    } 
    //Overload catch all 
    static Tuple<string, string> AttributeValues(object value) 
    { 
     return Tuple.Create("string", value.ToString()); 
    } 
    // Using John Skeet's Utf8StringWriter trick 
    // http://stackoverflow.com/questions/3871738/force-xdocument-to-write-to-string-with-utf-8-encoding/3871822#3871822 
    class Utf8StringWriter : System.IO.StringWriter 
    { 
     public override System.Text.Encoding Encoding { get { return System.Text.Encoding.UTF8; } } 
    } 

tùy ý: Thay đổi để tuyên bố:

let attributeVals = (Tuple<string,string>)AttributeValues((dynamic)param.Value) 

Điều đó sẽ hạn chế cuộc gọi động để chỉ dòng đó. Nhưng vì không có nhiều thứ khác xảy ra nên tôi nghĩ sẽ sạch hơn nếu không thêm dàn diễn viên bổ sung.

+0

Đó là một cách sử dụng thú vị của 'dynamic'. Tôi quên rằng đây là một trong những lựa chọn của chúng tôi. –

+0

Hầu hết các ngôn ngữ động không cung cấp phương thức quá tải, vì vậy ngay cả khi bạn quen với nó, bạn có xu hướng nghĩ rằng công văn đôi có liên quan nhiều hơn. Đó là một trong những tác dụng phụ ntat của C# là một ngôn ngữ tĩnh/động lai thực sự hiện nay. – jbtule

5
public string ConvertToXml() 
{ 
    var doc = new XDocument(
     new XElement("sd", 
      Parameters.Select(param => 
       new XElement("pr", 
        new XAttribute("tp", GetTypeName((dynamic)param.Value)), 
        new XAttribute("nm", param.Key), 
        new XAttribute("vl", GetValue((dynamic)param.Value)) 
        ) 
       ) 
      ) 
     ); 
    return doc.ToString(); 
} 

Mã này giả định rằng bạn đã quá tải phương pháp GetTypeName()GetValue() thực hiện như:

static string GetTypeName(long value) 
{ 
    return "int"; 
} 

static string GetTypeName(DateTime? value) 
{ 
    return "datetime"; 
} 

static string GetTypeName(object value) 
{ 
    return "string"; 
} 

static string GetValue(DateTime? value) 
{ 
    return value.Value.ToString("o"); 
} 

static string GetValue(object value) 
{ 
    return value.ToString(); 
} 

này sử dụng thực tế là khi sử dụng dynamic, sự quá tải đúng sẽ được chọn khi chạy.

Bạn không cần quá tải cho intshort, vì chúng có thể được chuyển đổi thành long (và chuyển đổi như vậy được coi là tốt hơn chuyển đổi thành object). Nhưng điều này cũng có nghĩa là các loại như ushortbyte sẽ nhận được tp trong số int.

Ngoài ra, chuỗi trả về không chứa khai báo XML, nhưng không có nghĩa là khai báo rằng chuỗi được mã hóa UTF-16 được mã hoá UTF-8. (Nếu bạn muốn lưu nó trong một tệp được mã hóa UTF-8 sau này, hãy trả lại và lưu XDocument sẽ tốt hơn và viết khai báo XML chính xác.)

Tôi nghĩ đây là một giải pháp tốt, vì nó tách biệt mối quan tâm vào các phương pháp khác nhau (thậm chí bạn có thể đặt GetTypeName()GetValue() quá tải vào các lớp khác nhau).

+0

đẹp một người bạn đời !! –

+0

Các mối quan tâm thực sự tách biệt? Chắc chắn trong các trường hợp được chỉ định, trường hợp 'int' và' string' chia sẻ cùng một mã GetValue, nhưng khi chúng ta nói về phân loại và tuần tự hóa dữ liệu thì những dữ liệu đó sẽ tách biệt, ngược lại có thể đúng không (ví dụ: sẽ chia sẻ cùng một phương thức GetTypeName nhưng có một GetValue khác)? Không bao giờ. – jbtule

+0

Lý do duy nhất tôi có thể tìm ra sự khác biệt là loại khác nhau liên quan đến kiểu SQL mà đối tượng này sử dụng cuối cùng. Nhưng có điểm công bằng. –

Các vấn đề liên quan