0

子クラス「job」のすべてのプロパティについて、CDATAタグでラップされたXMLにシリアル化するという醜い要件があります。仕様は次のとおりです:http: //www.indeed.com/intl/en/xmlinfo.html

私が見つけたすべての答えは、クラス全体ではなく、1つまたは2つのプロパティに適したアプローチを示唆しています。例:XmlSerializerを使用して文字列をCDATAとしてどのようにシリアル化しますか?

その記事のコメントの1つは、個々のクラスにIXmlSerializableを実装することを提案しています。私のジョブクラスのすべてのプロパティにはCDATAラッパーが必要であり、他のどこにもこの要件がないため、より良いアプローチのように思えます。ただし、これを行うときに利用できるシリアライザーをオーバーライドするプロパティごとのメンバーがあれば、さらに良いでしょう。

CDATAの実装を200行のコードから10行に減らす方法についてのアイデアはありますか?CDATA要件がなければ、これがいかに簡単であるかがわかります。

public class job {
    public string title { get; set; }
    public DateTime date { get; set; }
    public int referencenumber { get; set; }
    public string url { get; set; }
    public string company { get; set; }
    public string city { get; set; }
    public string state { get; set; }
    public string country { get; set; }
    public string postalcode { get; set; }
    public string description { get; set; }
    public string salary { get; set; }
    public string category { get; set; }
    public string experience { get; set; }

    public job(DataModel.PostedJob postedJob, UrlHelper urlHelper) {
        country = "US";
        category = "contract, project, consulting";
        title = postedJob.Title;
        date = postedJob.PostedOn;
        referencenumber = postedJob.Id;
        url = urlHelper.Action(MVC.SearchJobs.Index(postedJob.Id));
        company = postedJob.Company;
        city = postedJob.City;
        state = postedJob.State;
        postalcode = postedJob.PostalCode;
        description = postedJob.Description;
        experience = postedJob.MinYears.ToString() + "+ years";
    }

    public job() { }
}

編集:現在、ここに示すMvcContrib "XmlResult"コードスニペットを使用してこれをシリアル化していることに注意してください:コントローラーのアクションからXMLをActionResultとして返しますか?...プロパティ名自体の従来とは異なるケースは、上記でリンクされた仕様で必要とされるXMLスキーマを反映しています。ビューモデルのプロパティは通常、コンシューマーと緊密に結合されているため、適切だと思いましたが、自由に修正してください。

4

1 に答える 1

1

すべてが単純な場合は、LINQtoXMLを使用して独自のシリアライザーを作成できます。

これは非常に洗練されていないバージョンです...必要なすべてのタイプをカバーするようにする必要があります。また、必要に応じてnull参照を表す方法も検討する必要があります。

using System;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;

class Foo
{
    public string Name { get; set; }
    public int Value { get; set; }
    public DateTime When { get; set; }
}

class Test
{
    static void Main()
    {
        Foo foo = new Foo { 
            Name = "Jon", 
            Value = 5,
            When = DateTime.Now
        };

        XElement element = Serialize("Foo", foo);
        Console.WriteLine(element);
        Foo bar = Deserialize<Foo>(element);
        Console.WriteLine("Deserialized values:");
        Console.WriteLine("{0}, {1}, {2}", 
                          bar.Name, bar.Value, bar.When);
    }

    static XElement Serialize(XName elementName,
                              object value)
    {
        return new XElement(elementName,
          value.GetType()
               .GetProperties()
               .Select(p => new XElement
                   (p.Name, ConvertValue(p.GetValue(value, null)))));

    }

    static XCData ConvertValue(object value)
    {
        // This is a bit odd. It takes advantage of LINQ
        // to XML being nice with XElement, but not for
        // XCData
        XElement tmp = new XElement("ignored", value);
        return new XCData(tmp.Value);
    }

    static T Deserialize<T>(XElement element) where T : new()
    {
        T ret = new T();
        foreach (var sub in element.Elements())
        {
            var property = typeof(T).GetProperty(sub.Name.LocalName);
            SetProperty(property, sub, ret);
        }
        return ret;
    }

    static void SetProperty(PropertyInfo property,
                            XElement element,
                            object target)
    {
        // This is somewhat annoying. There should be a better
        // way...
        var type = property.PropertyType;
        object value;
        if (type == typeof(string))
        {
            value = (string) element;
        } 
        else if (type == typeof(int))
        {
            value = (int) element;
        }
        else if (type == typeof(DateTime))
        {
            value = (DateTime) element;
        }
        // ...
        else
        {
            throw new Exception("Can't convert " + type);
        }
        property.SetValue(target, value, null);
    }
}
于 2012-08-02T06:31:18.210 に答える