2016-11-14 2 views
3

Newtonsoft.JsonライブラリJsonConvert.DeserializeXmlNodeは、要素に属性がある場合、一貫性のない日時の結果を返します。ここでNewtonsoft.Json要素が属性を持つときにXmlDocumentの日付書式が矛盾する場合

は、あなたが見ることができるように発行し

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との間で変換を行っても、そのオプションは得られません。

また、実行時にどうなっているかわからないので、残念ながら - スキーマ生成クラスのJsonConvertを実行できません。

要素に属性があるときに、同じ形式を戻す方法を知っている人はいますか?

Json.NET 10.0.1で修正されたおかげで

答えて

3

更新

  • 修正 - 固定いくつかのXMLノード内の非文字列の値をデシリアライズ

を参照してくださいthis issuethis commit

オリジナル答え

これはJson.NETのXmlNodeConverterのバグのようです。 report an issueにすることもできます。

回避策は、JSONからXMLに変換するときに日付解析を無効にすることです。 は、JSONのすべての日付と時刻がすでにISO 8601の形式である限り、確実に動作します。それはあなたのテストケースに真であると思われますので、[OK]をする必要があります次のように

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

原因のバグのは、あなたが問題を報告することを決定した場合には、あります。

  • 属性なし:  {"timestamp":"2016-11-15T01:07:14"}あなたが気づいてきたように、Json.NETは異なった属性を持つ要素のテキスト値から属性なし要素のXMLテキスト値を表しています。

    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トークン値は、メソッドXmlNodeConverter.CreateElement()経由でXMLのDOMに追加されます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))); 
    
+0

ありがとうございました!私は確かに問題を報告します。 – bixarrio

関連する問題