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

Irf*_*nif 5 c# xml asp.net-mvc json asp.net-web-api

我的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);
}
Run Code Online (Sandbox Code Playgroud)

我将此 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";
}
Run Code Online (Sandbox Code Playgroud)

我收到错误:

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
Run Code Online (Sandbox Code Playgroud)

更新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}]
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

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

dbc*_*dbc 5

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

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

{
  "root": {
    "person": [
      {
        "name": "Alan"
      },
      {
        "name": "Louis"
      }
    ]
  }
}
Run Code Online (Sandbox Code Playgroud)

转换为XML如下:

<root>
  <person>
    <name>Alan</name>
  </person>
  <person>
    <name>Louis</name>
  </person>
</root>
Run Code Online (Sandbox Code Playgroud)

请注意,创建了一个外部<root>节点和一个重复的<person>节点序列——但中间没有任何内容?如果 JSON 中不存在具有"root"属性的外部对象,那么 Json.NET 将尝试创建具有多个<person>根元素的 XML。XML 标准不允许这样做,因为XML 标准只需要一个根元素。因此,JSON 数组必须包含在至少两层JSON 对象嵌套中才能成功转换为 XML(尽管其中一层可以通过指定外部根元素名称来实现JsonConvertDeserializeXmlNode(String, String))。

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

首先,从“如何将多个 TextReader 串在一起?”的答案中获取ChainedTextReader和。作者:雷克斯·M。使用它们创建以下扩展方法:public 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 
// /sf/ask/204795671/#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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并转换为XML如下:

var doc = JsonExtensions.DeserializeXmlNode(data, "root", "array"); 
Run Code Online (Sandbox Code Playgroud)

使用问题中的 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>
Run Code Online (Sandbox Code Playgroud)

工作示例.Net fiddle