0

I'm using ASP.Net Core 2.2. My app gets data from an API which produces XML Content. In other side my app expect Json data so I have to convert XML to Json here is what I've done so far in MyController

public async Task<IActionResult> Index()
{
    List<ProductType> ProductTypes = new List<ProductType>();

    using (var httpClient = new HttpClient())
    {
            using (var response = await httpClient.GetAsync("https://localhost:44374/api/productTypes"))
            {
               string text = await response.Content.ReadAsStringAsync();
               XmlDocument doc = new XmlDocument();
               doc.LoadXml(text);
               var json = JsonConvert.SerializeXmlNode(doc);
               ProductTypes = JsonConvert.DeserializeObject<List<ProductType>>(json);
            }

    }
    return View("index", ProductTypes);
}

And this is ProductType model

public class ProductType
{

    public int Id { get; set; }
    public string Name { get; set; }
    public string Image { get; set; }

}

This is the content of Json variable in my controller when I get data from the API

"{\"ArrayOfProductType\":{\"@xmlns:i\":\"http://www.w3.org/2001/XMLSchema-instance\",\"@xmlns\":\"http://schemas.datacontract.org/2004/07/MyApi.Core.Models\",\"ProductType\":[{\"Id\":\"1\",\"Image\":\"/images/pizza.png\",\"Name\":\"Pizza\"},{\"Id\":\"2\",\"Image\":\"/images/burger.png\",\"Name\":\"Burger\"},{\"Id\":\"3\",\"Image\":\"/images/ad-salad.png\",\"Name\":\"Salad\"},{\"Id\":\"4\",\"Image\":\"/images/drinks.png\",\"Name\":\"Drinks\"}]}}"

And here is Index view

@model IEnumerable<ProductType>
@{
    ViewData["Title"] = "index";
}

<table class="table table-sm table-striped table-bordered m-2">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var r in Model)
        {
            <tr>
                <td>@r.Id</td>
                <td>@r.Name</td>
            </tr>
        }
    </tbody>
</table>

But when I run the program i get this runtime error

JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Pizza.Models.ProductType]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'ArrayOfProductType', line 1, position 22.

What I'm missing here?

I found this link that indicate the problem but what is the solution?

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1

Arian Shahalami
  • 1,369
  • 2
  • 13
  • 25
  • Have you noticed your Json doesn't match your model? – Masoud Andalibi Aug 11 '19 at 09:32
  • Take a good look at your `json` variable. It is way more than a `List` ... it starts with an `"{\"ArrayOfProductType\": {...}}"` – Asons Aug 11 '19 at 09:41
  • @LGSon is there anyway to put that array into a list of ProductTypes ? – Arian Shahalami Aug 11 '19 at 09:45
  • 1
    If you create a dummy `List` and serialize it, and then compare with the one you get from your XML, you'll see what is wrong and why it doesn't work. From there, in terms of _an object_, the linked answer will most likely make more sense, where you will need a temporary object to get your list from. – Asons Aug 11 '19 at 10:01
  • @Tseng This is practising my frined. If u work in a team, senior developer gives u an API which produce XML and another consumer app which only accept Json results. That's why I'm trying to do this. – Arian Shahalami Aug 11 '19 at 11:38
  • @ArianShahalami: And which api is consuming the JSON? You shown nothing in your example, you convert it to a type to use inside a view. Consuming JSOn would mean, that you use WebApi (which returns json or xml, depending on content negotiation) rather than MVC. So either you got the task wrong or you're stuck at the XY-Problem – Tseng Aug 11 '19 at 11:48
  • @Tseng Man it was unnecessary to bring code which is not relevant to my problem. If u are interested in such problem read the two first paragraph of this link and u will realize the problem. https://refactoring.guru/design-patterns/adapter – Arian Shahalami Aug 11 '19 at 12:01

2 Answers2

2

Why using XmlDocument? Are you receiving XML format from API? As indicated in the comments, first it's a good idea to test your Index() with a dummy list.

you might try this and modify your controller's Index():

string text = await response.Content.ReadAsStringAsync();
ProductTypes= JsonConvert.DeserializeAnonymousType<List<ProductType>>(text , ProductTypes);

You can define a model DTO to wrap and transfer data. But in the first place, make sure your json matches the model.

*Note: If your API gives out only xml, you need to create some nested model classes to simulate the nested structure of the xml you receive. This thread shows a good example: How do Deserialize XML to json and json to c# classes?

A. Nadjar
  • 2,440
  • 2
  • 19
  • 20
  • Tnx for participation. Could u tell me what is the best approach in this situation? Where the API produces only XML and the consumer app accepts only Json result? – Arian Shahalami Aug 11 '19 at 11:42
  • Thank you. My previous APIS have been json. Since your API only gives out XML, you need to create several model classes to simulate the nested structure of XML tags in the result received from API. I believe this is the best thread that shows you how to do it : https://stackoverflow.com/questions/47372027/how-do-deserialize-xml-to-json-and-json-to-c-sharp-classes – A. Nadjar Aug 11 '19 at 13:24
0

The reason your json is not being deserialized is because you don't have a list in the response only, it is an object containing a list of products and two other json variables. according to the response you posted, Your models should Look like this:

public class ProductType
{
    public string Id { get; set; }
    public string Image { get; set; }
    public string Name { get; set; }
}

public class ArrayOfProductType
{
    [JsonProperty(PropertyName = "@xmlns:i")]
    public string xmlnsi { get; set; }
    [JsonProperty(PropertyName = "@xmlns")]
    public string xmlns { get; set; }
    public List<ProductType> ProductType { get; set; }
}

public class RootObject
{
    public ArrayOfProductType ArrayOfProductType { get; set; }
}

then deserialize the object like this:

var result = JsonConvert.DeserializeObject<RootObject>(json);
Jamshaid K.
  • 3,555
  • 1
  • 27
  • 42