【问题标题】:XmlNodeConverter can only convert JSON that begins with an objectXmlNodeConverter 只能转换以对象开头的 JSON
【发布时间】: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";
}

我得到错误:

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

同样在 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 请查看更新的答案

标签: c# json xml asp.net-mvc asp.net-web-api


【解决方案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 MHow 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://stackoverflow.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

【讨论】:

  • 帮助很大,谢谢
猜你喜欢
  • 1970-01-01
  • 2022-01-01
  • 2022-01-18
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-15
  • 1970-01-01
相关资源
最近更新 更多