当元素具有属性时,Newtonsoft.Json JsonConvert到XmlDocument日期格式不一致

时间:2016-11-14 13:18:38

标签: c# json xml

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,因为我不知道它在执行时可能是什么。

当元素具有属性时,是否有人知道如何获得相同的格式?

由于

1 个答案:

答案 0 :(得分:3)

<强>更新

在Json.NET 10.0.1中修复:

  • 修复 - 修复了在某些XML节点中反序列化非字符串值的问题

请参阅this issuethis commit

原始回答

这似乎是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));
            }
        }
    

    它会调用ConvertTokenToXmlValue()

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