簡體   English   中英

如何從C#“字符串”中的Rails-XML反序列化可為空的日期時間,不是有效的AllXsd值。”

[英]How to deserialize a nullable datetime from Rails-XML in C# “The string '' is not a valid AllXsd value.”

我正在從Rails API中獲取XML,但是在嘗試對其進行反序列化時,我始終收到錯誤消息“字符串”不是有效的AllXsd值。”

我覺得這應該真的很容易並且瑣碎,而且我只是缺少一些或其他與XML相關的屬性,但是在google上找不到任何東西。 那里的所有示例似乎都使用字符串作為Date-Time值,然后自行進行內部解析以獲取實際的可為null的DateTime。 那行得通,但感覺很混亂,下次有人看到它時可能看起來很奇怪,這時他們可能會嘗試用一個簡單的屬性替換它,或者在測試期間錯過了null或卡在了我現在的位置。

請原諒以下示例的冗長性,但我嘗試將其減少到實際實現中的相關代碼量最小。

class Program
{
    static void Main(string[] args)
    {
        var samples = new Dictionary<string, DateTime?>{
            { // from Rails with populated datetime value
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<response>\r\n  <latest-playing-at type=\"datetime\">2016-07-22T15:24:22+00:00</latest-playing-at>\r\n</response>\r\n"
                ,new DateTime(2016,07,22,17,24,22,DateTimeKind.Local)},
            { // from Rails with nil datetime value (THIS IS THE CASE I CARE ABOUT)
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<response>\r\n  <latest-playing-at nil=\"true\"/>\r\n</response>\r\n"
                ,(DateTime?)null},
            { // (Test) Serialized from dotnet with null value
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<response xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n  <latest-playing-at xsi:nil=\"true\" />\r\n</response>"
                    ,(DateTime?)null},
        };
        foreach (var sample in samples)
        {
            var xml = sample.Key;
            var expected = sample.Value;
            try
            {
                var testClass = DeserializeFromString<TestClass>(xml);
                Console.WriteLine("Expected / Actual : {0} / {1}", expected, testClass.LatestPlayingAt);
                Console.WriteLine(
                    DateTime.Equals(expected, testClass.LatestPlayingAt) ? "OK" : "Different" // ok to be different depening on time zones
                );
            }
            catch (Exception exc)
            {
                Console.WriteLine("Error: {0}", exc.Message);
                Console.WriteLine(exc);
            }
        }
        Console.ReadKey(true);
    }

    public static T DeserializeFromString<T>(string inputXML)
    {
        using (MemoryStream inStream = new MemoryStream(new UTF8Encoding(false).GetBytes(inputXML)))
        {
            T result = DeserializeFromStream<T>(inStream);
            return result;
        }
    }

    public static T DeserializeFromStream<T>(Stream inStream)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        return (T)serializer.Deserialize(inStream);
    }
}

[XmlRoot("response")]
public class TestClass
{
    //[XmlElement("latest-playing-at")]                                                                                          // The string '' is not a valid AllXsd value.
    //[XmlElement("latest-playing-at", Type = typeof(DateTime?))]                                                                // The string '' is not a valid AllXsd value.
    //[XmlElement("latest-playing-at", Type = typeof(DateTime?), IsNullable = true)]                                             // The string '' is not a valid AllXsd value.
    //[XmlElement("latest-playing-at", Type = typeof(Nullable<DateTime>), IsNullable = true, Form = XmlSchemaForm.None)]         // The string '' is not a valid AllXsd value.
    [XmlElement("latest-playing-at", Type = typeof(Nullable<DateTime>), IsNullable = true, Form = XmlSchemaForm.Unqualified)]    // The string '' is not a valid AllXsd value.
    public DateTime? LatestPlayingAt { get; set; }
}

問題是nil (與xsi:nil相反)沒有任何特殊含義,它只是另一個屬性。 您的XML恰好使用它來表示相同的事實的事實對於序列化器來說沒有任何意義。

如我所見,您有兩個選擇:

  1. 與字符串串行化,並自己處理解析和轉換為字符串。
  2. 預處理XML,以使用xsi:nil替換所有nil實例。

第一種選擇是相當自我解釋的(您在問題中提到了它)。 第二個可以用這樣的代碼完成:

var doc = XDocument.Parse(xml);

XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";

var nilAttributes = doc.Descendants()
    .Attributes("nil")
    .Where(x => x.Value == "true");

foreach (var attribute in nilAttributes)
{
    var element = attribute.Parent;
    attribute.Remove();            
    element.Add(new XAttribute(xsi + "nil", true));
}

然后,您可以將由doc.CreateReader()創建的XmlReader傳遞給序列化程序,或者通過調用doc.ToString()獲得新的XML字符串。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM