我从Rails API获取XML,但在尝试反序列化时,我不断收到错误"字符串''不是有效的AllXsd值。"
我觉得这应该是非常容易和琐碎的,我只是缺少一些或其他XML相关的属性,但我在谷歌上找不到任何东西。所有示例似乎都使用字符串作为Date-Time值,然后自己进行内部解析以获得实际可为空的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; }
}
答案 0 :(得分:1)
问题是nil
(与xsi:nil
相对)没有任何特殊含义,它只是另一个属性。您的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()
传递给序列化程序,或通过调用doc.ToString()
获取新的XML字符串。