XmlNodeConverter 只能转换以对象开头的 JSON

Posted

技术标签:

【中文标题】XmlNodeConverter 只能转换以对象开头的 JSON【英文标题】:XmlNodeConverter can only convert JSON that begins with an object 【发布时间】:2018-07-24 23:07:15 【问题描述】:

我的webapi方法是:

public JsonResult<List<MyClass>> PullData()

    List<MyClass> data = new List<MyClass>();
    data = db.TableName.Select(x => new MyClass
    
        Id = x.Id,
        IsActive = x.IsActive,
        //other attribute..
    ).ToList();

    return Json(data);

我将这个 webapi 用作:

public async Task<string> Index()

    string apiUrl = "http://localhost:90/api/Scheduler/pulldata";

    using (HttpClient client = new HttpClient())
    
        client.BaseAddress = new Uri(apiUrl);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

        HttpResponseMessage response = await client.GetAsync(apiUrl);
        if (response.IsSuccessStatusCode)
        
            var data = await response.Content.ReadAsStringAsync();

            JsonConvert.DeserializeXmlNode(data, "root"); //exception: XmlNodeConverter can only convert JSON that begins with an object.
        
    
    return "Error";

我得到错误:

同样在 api 消费方法(即Index)中,当我调试并看到var data = await response.Content.ReadAsStringAsync(); 中的数据为JSON Visualizer 时,它显示数据正常。

但是当我使用 XML 可视化工具时,它不显示数据。

更新: 数据太大。我不能分享。这是数据的屏幕截图。

更新 2:

这里是开始的部分json数据:

["LearningActivityKey":2122,"ModuleName":"certName","ModuleVersion":1.0,"ModuleDescription":"<p><span style=\"background-color:rgb(240, 240, 240); font-family:archivo narrow,helvetica,arial,sans-serif; font-size:16px; line-height:20px; white-space:pre-line\">Learn SAP

更新 3:

我已将 webapi 方法 PullData() 更改为只发送两条记录,以便我们可以轻松地查看问题是否与 json 数据有关。

完整的数据是:

["LearningActivityKey":2122,"ModuleName":"certName","ModuleVersion":0.0,"ModuleDescription":null,"BadgeName":null,"BadgeVersion":null,"BadgeDescription":null,"MozillaBadge":null,"LearningActivityName":null,"LearningActivityDescription":null,"StepName":null,"StepVersion":null,"StepDescription":null,"IsActive":false,"IsPublished":false,"CreatedDate":"0001-01-01T00:00:00","ModifiedDate":null,"LearningActivityKey":2122,"ModuleName":"certName","ModuleVersion":0.0,"ModuleDescription":null,"BadgeName":null,"BadgeVersion":null,"BadgeDescription":null,"MozillaBadge":null,"LearningActivityName":null,"LearningActivityDescription":null,"StepName":null,"StepVersion":null,"StepDescription":null,"IsActive":false,"IsPublished":false,"CreatedDate":"0001-01-01T00:00:00","ModifiedDate":null]

我在https://jsonformatter.curiousconcept.com/ 中粘贴了数据,上面写着:

XML Visualizer 仍然没有显示任何数据。

【问题讨论】:

你也可以分享一下 JSON 吗? @FaizanRabbani,请查看更新后的答案。让我知道我还能提供什么以更好地理解。 你应该试试 JsonConvert.DeserializeObject() 你能把json字符串的开头贴出来,这样我们就可以理解为什么它不是以对象开头的。可能是开头的空格或换行符。 @FaizanRabbani 我也试过JsonConvert.DeserializeObject()。我收到错误:Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'MyClass' because the type requires a JSON object (e.g. "name":"value") to deserialize correctly.。 @jdweng 请查看更新的答案 【参考方案1】:

例外不言自明:除非根标记是对象,否则无法将 JSON 转换为 XML,即使您使用 JsonConvertDeserializeXmlNode(String, String) 方法指定外部根元素名称也是如此。

至于为什么这是真的,文档页面 Converting between JSON and XML 显示 JSON 数组被转换为 XML 元素的重复序列没有添加外部容器元素。 IE。像这样的 JSON(从文档中简化):


  "root": 
    "person": [
      
        "name": "Alan"
      ,
      
        "name": "Louis"
      
    ]
  

转成XML如下:

<root>
  <person>
    <name>Alan</name>
  </person>
  <person>
    <name>Louis</name>
  </person>
</root>

请注意,创建了一个外部&lt;root&gt; 节点,以及一个重复的&lt;person&gt; 节点序列——但两者之间什么都没有?如果 JSON 中没有具有 "root" 属性的外部对象,那么 Json.NET 将尝试创建具有多个 &lt;person&gt; 根元素的 XML。 XML standard 不允许这样做,它只需要一个根元素。因此,JSON 数组似乎必须包含在至少 两个 级别的 JSON 对象嵌套中才能成功转换为 XML(尽管其中一个级别可以通过 JsonConvertDeserializeXmlNode(String, String) 指定外部根元素名称来实现)。

作为一种解决方法,您可以引入以下扩展方法以将 JSON 嵌套在额外级别的对象中。

首先,从Rex M 对How to string multiple TextReaders together? 的回答中获取ChainedTextReaderpublic static TextReader Extensions.Concat(this TextReader first, TextReader second)。使用它们,创建以下扩展方法:

public static partial class JsonExtensions

    public static XmlDocument DeserializeXmlNode(string json, string rootName, string rootPropertyName)
    
        return DeserializeXmlNode(new StringReader(json), rootName, rootPropertyName);
    

    public static XmlDocument DeserializeXmlNode(TextReader textReader, string rootName, string rootPropertyName)
    
        var prefix = "" + JsonConvert.SerializeObject(rootPropertyName) + ":";
        var postfix = "";

        using (var combinedReader = new StringReader(prefix).Concat(textReader).Concat(new StringReader(postfix)))
        
            var settings = new JsonSerializerSettings
            
                Converters =  new Newtonsoft.Json.Converters.XmlNodeConverter()  DeserializeRootElementName = rootName ,
                DateParseHandling = DateParseHandling.None,
            ;
            using (var jsonReader = new JsonTextReader(combinedReader)  CloseInput = false, DateParseHandling = DateParseHandling.None )
            
                return JsonSerializer.CreateDefault(settings).Deserialize<XmlDocument>(jsonReader);
            
        
    


// Taken from 
// https://***.com/questions/2925652/how-to-string-multiple-textreaders-together/2925722#2925722

public static class Extensions

    public static TextReader Concat(this TextReader first, TextReader second)
    
        return new ChainedTextReader(first, second);
    

    private class ChainedTextReader : TextReader
    
        private TextReader first;
        private TextReader second;
        private bool readFirst = true;

        public ChainedTextReader(TextReader first, TextReader second)
        
            this.first = first;
            this.second = second;
        

        public override int Peek()
        
            if (readFirst)
            
                return first.Peek();
            
            else
            
                return second.Peek();
            
        

        public override int Read()
        
            if (readFirst)
            
                int value = first.Read();
                if (value == -1)
                
                    readFirst = false;
                
                else
                
                    return value;
                
            
            return second.Read();
        

        public override void Close()
        
            first.Close();
            second.Close();
        

        protected override void Dispose(bool disposing)
        
            base.Dispose(disposing);
            if (disposing)
            
                first.Dispose();
                second.Dispose();
            
        
    

并转换成XML如下:

var doc = JsonExtensions.DeserializeXmlNode(data, "root", "array"); 

使用您问题中的 JSON,生成以下 XML:

<root>
  <array>
    <LearningActivityKey>2122</LearningActivityKey>
    <ModuleName>certName</ModuleName>
    <!-- Additional properties omitted -->
  </array>
  <array>
    <LearningActivityKey>2122</LearningActivityKey>
    <ModuleName>certName</ModuleName>
    <!-- Additional properties omitted -->
  </array>
</root>

工作样本.Net fiddle。

【讨论】:

帮助很大,谢谢

以上是关于XmlNodeConverter 只能转换以对象开头的 JSON的主要内容,如果未能解决你的问题,请参考以下文章

带有 TypeError 的 TensorRT 对象检测:只能将整数标量数组转换为标量索引

在转换为 XML 之前更改 JSON 对象中的属性名称

将 JSON 字符串转换为 Gson 对象

如何将 HttpRequestBase 转换为 HttpRequest 对象?

如何让一个字符串(由多个小字符串,中间以逗号分隔开)转换成stringlist类

面向对象思想设计原则