Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
521 views
in Technique[技术] by (71.8m points)

c# - XmlNodeConverter can only convert JSON that begins with an object

My webapi method is:

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

And I'm consuming this webapi as:

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

I get error:

XmlNodeConverter can only convert JSON that begins with an object.

Also In api consumption method (i.e, Index) when I debug and see data in var data = await response.Content.ReadAsStringAsync(); as JSON Visualizer it shows data fine.

enter image description here

But when I do XML visualizer, it doesn't show data. enter image description here

Update: The data is too large. I can't share it. Here is the screen shot of data. enter image description here

Update 2:

Here is a part of json data from begining:

[{"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

Update 3:

I have changed the webapi method PullData() to send only two records, so that we can easily visualize wethere the issue is with json data.

Complete data is:

[{"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}]

I pasted data in https://jsonformatter.curiousconcept.com/ and it says:

enter image description here

And XML Visualizer still doesn't show any data.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The exception is self-explanatory: you cannot convert JSON to XML unless the root token is an object, even if you use the JsonConvertDeserializeXmlNode(String, String) method to specify an outer root element name.

As to why this is true, the documentation page Converting between JSON and XML shows that a JSON array is converted to a repeating sequence of XML elements without an added outer container element. I.e. JSON like this (simplified from the documentation):

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

Is converted to XML as follows:

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

Notice that an outer <root> node is created, and a repeated sequence of <person> nodes -- but nothing in between? If there were no outer object with a "root" property in the JSON then Json.NET would have tried to create XML with multiple <person> root elements. This is disallowed by the XML standard which requires exactly one root element. Thus it appears a JSON array must be contained within at least two levels of JSON object nesting to be successfully converted to XML (although one of those levels could come by specifying an outer root element name via JsonConvertDeserializeXmlNode(String, String)).

As a workaround, you can introduce the following extension methods to nest your JSON in an extra level of object.

First, grab ChainedTextReader and public static TextReader Extensions.Concat(this TextReader first, TextReader second) from the answer to How to string multiple TextReaders together? by Rex M. Using them, create the following extension methods:

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

And convert to XML as follows:

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

Using the JSON from your question, the following XML is generated:

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

Working sample .Net fiddle.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...