2016-08-04 5 views
0

Я возвращаю XML из Rails API, но при попытке десериализации он продолжает получать ошибку «Строка» не является допустимым значением AllXsd. »Как десериализировать нулевое datetime из Rails-XML в C# «Строка '' не является допустимым значением AllXsd."

Я чувствую, что это должно быть очень просто и тривиально и что я просто пропускаю тот или иной атрибут, связанный с XML, но я не нахожу ничего, когда смотрю на google. Все примеры там, кажется, используют строку для значения Date-Time, а затем выполняют внутренний синтаксический анализ, чтобы получить фактическое значение DateTime с нулевым значением. Это будет работать, но будет беспорядочно и, вероятно, будет выглядеть странно в следующий раз, когда кто-то его увидит, и в этот момент они, вероятно, попытаются заменить его простым свойством и либо пропустить нуль во время тестирования, либо застрять там, где я сейчас.

Простите многословие следующего примера, но я попытался уменьшить его до наименьшего количества связанного кода из нашей реальной реализации.

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; } 
} 

ответ

1

Этот вопрос nil (в отличие от xsi:nil) не имеет особого смысла, это просто еще один атрибут. Тот факт, что ваш XML использует его для обозначения того же, не представляет интереса для сериализатора.

Как я понимаю, у вас есть два варианта:

  1. сериализация и из строки и обрабатывать синтаксический анализ и преобразование в строку самостоятельно.
  2. Предварительно обработать XML, чтобы заменить все экземпляры nil на xsi: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)); 
} 

Вы можете передать XmlReader созданный doc.CreateReader() к serialiser или получить новую строку XML, вызвав doc.ToString().

Смежные вопросы