Newtonsoft.Json
图书馆'当元素具有属性时,JsonConvert.DeserializeXmlNode
会给出不一致的日期时间结果。
这是一个演示问题的小例子
public void Main(string[] args)
{
var now = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");
var xml = $"<timestamp>{now}</timestamp>";
Debug.WriteLine(xml);
// <timestamp>2016-11-14T14:51:32</timestamp>
var json = XmlToJson(xml);
Debug.WriteLine(json);
// {"timestamp":"2016-11-14T14:51:32"}
var good = JsonToXml(json);
Debug.WriteLine(good);
// <?xml version="1.0" encoding="utf-8"?><timestamp>2016-11-14T14:51:32</timestamp>
var xml_with_attr = $"<timestamp id=\"1\">{now}</timestamp>";
Debug.WriteLine(xml_with_attr);
// <timestamp id="1">2016-11-14T14:51:32</timestamp>
var json_with_attr = XmlToJson(xml_with_attr);
Debug.WriteLine(json_with_attr);
// {"timestamp":{"@id":"1","#text":"2016-11-14T14:51:32"}}
var bad = JsonToXml(json_with_attr);
Debug.WriteLine(bad);
// <?xml version="1.0" encoding="utf-8"?><timestamp id="1">2016-11-14 2:51:32 PM</timestamp>
}
private string XmlToJson(string xml)
{
var doc = new XmlDocument();
doc.LoadXml(xml);
var json = JsonConvert.SerializeXmlNode(doc);
return json;
}
private string JsonToXml(string json)
{
var doc = JsonConvert.DeserializeXmlNode(json);
var xml = string.Empty;
var settings = new XmlWriterSettings
{
CloseOutput = true,
Encoding = Encoding.UTF8,
};
using (var ms = new MemoryStream())
using (var xw = XmlWriter.Create(ms, settings))
{
doc.WriteTo(xw);
xw.Flush();
xml = settings.Encoding.GetString(ms.ToArray());
}
return xml;
}
如您所见,bad
日期的格式与之前的所有结果不同。遗憾的是,一旦根据模式验证了xml,它就会导致模式验证失败。
我知道DateTimeConverter
的内容,但转换为XmlDocument和从XmlDocument转换并不能给我这个选项。
我还可以 - 遗憾的是 - 不要在模式生成的类上执行JsonConvert
,因为我不知道它在执行时可能是什么。
当元素具有属性时,是否有人知道如何获得相同的格式?
由于
答案 0 :(得分:3)
<强>更新强>
在Json.NET 10.0.1中修复:
原始回答
这似乎是Json.NET XmlNodeConverter
中的一个错误。您可能需要report an issue。
解决方法是在从JSON转换为XML时禁用日期解析。请注意,只有JSON中的所有日期和时间都是ISO 8601格式时,才能可靠地。因为在您的测试用例中似乎是正确的,所以您应该没问题:
private static string JsonToXml(string json)
{
var settings = new JsonSerializerSettings
{
Converters = { new Newtonsoft.Json.Converters.XmlNodeConverter() },
DateParseHandling = DateParseHandling.None,
};
var doc = JsonConvert.DeserializeObject<XmlDocument>(json, settings);
var xmlSettings = new XmlWriterSettings
{
CloseOutput = true,
Encoding = Encoding.UTF8,
};
string xml;
using (var ms = new MemoryStream())
using (var xw = XmlWriter.Create(ms, xmlSettings))
{
doc.WriteTo(xw);
xw.Flush();
xml = xmlSettings.Encoding.GetString(ms.ToArray());
}
return xml;
}
如果您决定报告问题,错误的原因如下所示。正如您所注意到的,Json.NET表示没有属性的元素的XML文本值与具有属性的元素的文本值不同:
无属性:{"timestamp":"2016-11-15T01:07:14"}
。
在这种情况下,日期字符串的JSON标记值将通过XmlNodeConverter.CreateElement()
方法添加到XML DOM中:
if (reader.TokenType == JsonToken.String
|| reader.TokenType == JsonToken.Integer
|| reader.TokenType == JsonToken.Float
|| reader.TokenType == JsonToken.Boolean
|| reader.TokenType == JsonToken.Date)
{
string text = ConvertTokenToXmlValue(reader);
if (text != null)
{
element.AppendChild(document.CreateTextNode(text));
}
}
private string ConvertTokenToXmlValue(JsonReader reader)
{
if (reader.TokenType == JsonToken.String)
{
return (reader.Value != null) ? reader.Value.ToString() : null;
}
else if (reader.TokenType == JsonToken.Integer)
{
#if !(NET20 || NET35 || PORTABLE || PORTABLE40)
if (reader.Value is BigInteger)
{
return ((BigInteger)reader.Value).ToString(CultureInfo.InvariantCulture);
}
#endif
return XmlConvert.ToString(Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture));
}
else if (reader.TokenType == JsonToken.Float)
{
if (reader.Value is decimal)
{
return XmlConvert.ToString((decimal)reader.Value);
}
if (reader.Value is float)
{
return XmlConvert.ToString((float)reader.Value);
}
return XmlConvert.ToString(Convert.ToDouble(reader.Value, CultureInfo.InvariantCulture));
}
else if (reader.TokenType == JsonToken.Boolean)
{
return XmlConvert.ToString(Convert.ToBoolean(reader.Value, CultureInfo.InvariantCulture));
}
else if (reader.TokenType == JsonToken.Date)
{
#if !NET20
if (reader.Value is DateTimeOffset)
{
return XmlConvert.ToString((DateTimeOffset)reader.Value);
}
#endif
DateTime d = Convert.ToDateTime(reader.Value, CultureInfo.InvariantCulture);
#if !PORTABLE
return XmlConvert.ToString(d, DateTimeUtils.ToSerializationMode(d.Kind));
#else
return XmlConvert.ToString(d);
#endif
}
else if (reader.TokenType == JsonToken.Null)
{
return null;
}
else
{
throw JsonSerializationException.Create(reader, "Cannot get an XML string value from token type '{0}'.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
}
}
将JSON标记值转换为XML值有很多逻辑,并且在将日期和时间转换为XML时正在做正确的事。
属性:{"timestamp":{"@id":"1","#text":"2016-11-15T01:07:14"}}
但是在这种情况下,当前的JSON标记值按原样附加到方法DeserializeValue()
中的XML DOM中:
private void DeserializeValue(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, string propertyName, IXmlNode currentNode)
{
switch (propertyName)
{
case TextName:
currentNode.AppendChild(document.CreateTextNode(reader.Value.ToString()));
break;
如您所见,缺少转换逻辑,而是使用ToString()
。这就是错误。
用以下内容替换该行可以解决您的问题:
currentNode.AppendChild(document.CreateTextNode(ConvertTokenToXmlValue(reader)));