Tag Archives: JSON

XML + JSON Output for Web APIs in ASP .NET Core 3.1

By Shahed C on June 22, 2020

This is the twenty-fourth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

X is for XML + JSON Output

XML (eXtensible Markup Language) is a popular document format that has been used for a variety of applications over the years, including Microsoft Office documents, SOAP Web Services, application configuration and more. JSON (JavaScript Object Notation) was derived from object literals of JavaScript, but has also been used for storing data in both structured and unstructured formats, regardless of the language used. In fact, ASP .NET Core applications switched from XML-based .config files to JSON-based .json settings files for application configuration.

Returning XML/JSON format from a Web API

Returning JsonResult and IActionResult

Before we get into XML output for your Web API, let’s start off with JSON output first, and then we’ll get to XML. If you run the Web API sample project in the NetLearner repository, you’ll notice a LearningResourcesController.cs file that represents a “Learning Resources Controller” that exposes API endpoints. These endpoints can serve up both JSON and XML results of Learning Resources, i.e. blog posts, tutorials, documentation, etc.

Run the application and navigate to the following endpoint in an API testing tool, e.g. Postman:

  • https://localhost:44350/api/LearningResources
Sample JSON data in Postman
Sample JSON data in Postman

This triggers a GET request by calling the LearningResourcesController‘s Get() method:

  // GET: api/LearningResources
 [HttpGet]
 public JsonResult Get()
 {
    return new JsonResult(_sampleRepository.LearningResources());
 }

In this case, the Json() method returns a JsonResult object that serializes a list of Learning Resources. For simplicity, the _sampleRepository object’s LearningResources() method (in SampleRepository.cs) returns a hard-coded list of LearningResource objects. Its implementation here isn’t important, because you would typically retrieve such values from a persistent data store, preferably through some sort of service class.

public List<LearningResource> LearningResources()
{
   ... 
   return new List<LearningResource>
   {
      new LearningResource
      {
         Id= 1,
         Name= "ASP .NET Core Docs",
         Url = "https://docs.microsoft.com/aspnet/core",
         ...
      },
      ... 
   }
}

The JSON result looks like the following, where a list of learning resources are returned:

[
    {
        "id": 1,
        "name": "ASP .NET Core Docs",
        "url": "https://docs.microsoft.com/aspnet/core",
        "resourceListId": 1,
        "resourceList": {
            "id": 1,
            "name": "RL1",
            "learningResources": []
        },
        "contentFeedUrl": null,
        "learningResourceTopicTags": null
    },
    {
        "id": 2,
        "name": "Wake Up And Code!",
        "url": "https://WakeUpAndCode.com",
        "resourceListId": 1,
        "resourceList": {
            "id": 1,
            "name": "RL1",
            "learningResources": []
        },
        "contentFeedUrl": "https://WakeUpAndCode.com/rss",
        "learningResourceTopicTags": null
    }
]

Instead of specifically returning a JsonResult, you could also return a more generic IActionResult, which can still be interpreted as JSON. Run the application and navigate to the following endpoint, to include the action method “search” folllowed by a QueryString parameter “fragment” for a partial match.

  • https://localhost:44350/api/LearningResources/search?fragment=Wa
 Sample JSON data with search string
Sample JSON data with search string

This triggers a GET request by calling the LearningResourceController‘s Search() method, with its fragment parameter set to “Wa” for a partial text search:

// GET: api/LearningResources/search?fragment=Wa
[HttpGet("Search")]
public IActionResult Search(string fragment)
{
   var result = _sampleRepository.GetByPartialName(fragment);
   if (!result.Any())
   {
      return NotFound(fragment);
   }
   return Ok(result);
}

In this case, the GetByPartialName() method returns a List of LearningResources objects that are returned as JSON by default, with an HTTP 200 OK status. In case no results are found, the action method will return a 404 with the NotFound() method.

public List<LearningResource> GetByPartialName(string nameSubstring)
{
   return LearningResources()
      .Where(lr => lr.Title
         .IndexOf(nameSubstring, 0, StringComparison.CurrentCultureIgnoreCase) != -1)
      .ToList();
}

The JSON result looks like the following, which includes any learning resource that partially matches the string fragment provided:

[
    {
        "id": 2,
        "name": "Wake Up And Code!",
        "url": "https://WakeUpAndCode.com",
        "resourceListId": 1,
        "resourceList": {
            "id": 1,
            "name": "RL1",
            "learningResources": []
        },
        "contentFeedUrl": "https://WakeUpAndCode.com/rss",
        "learningResourceTopicTags": null
    }
]

Returning Complex Objects

An overloaded version of the Get() method takes in a “listName” string parameter to filter results by a list name for each learning resource in the repository. Instead of returning a JsonResult or IActionResult, this one returns a complex object (LearningResource) that contains properties that we’re interested in.

// GET api/LearningResources/RL1
[HttpGet("{listName}")]
public LearningResource Get(string listName)
{
   return _sampleRepository.GetByListName(listName);
}

The GetByListName() method in the SampleRepository.cs class simply checks for a learning resource by the listName parameter and returns the first match. Again, the implementation is not particularly important, but it illustrates how you can pass in parameters to get back JSON results.

public LearningResource GetByListName(string listName)
{
   return LearningResources().FirstOrDefault(lr => lr.ResourceList.Name == listName);
}

While the application is running, navigate to the following endpoint:

  • https://localhost:44350/api/LearningResources/RL1
Sample JSON data with property filter
Sample JSON data with property filter

This triggers another GET request by calling the LearningResourcesController‘s overloaded Get() method, with the listName parameter. When passing the list name “RL1”, this returns one item, as shown below:

{
    "id": 1,
    "name": "ASP .NET Core Docs",
    "url": "https://docs.microsoft.com/aspnet/core",
    "resourceListId": 1,
    "resourceList": {
        "id": 1,
        "name": "RL1",
        "learningResources": []
    },
    "contentFeedUrl": null,
    "learningResourceTopicTags": null
}

Another example with a complex result takes in a similar parameter via QueryString and checks for an exact match with a specific property. In this case the Queried() action method calls the repository’s existing GetByListName() method to find a specific learning resource by its matching list name.

// GET: api/LearningResources/queried?listName=RL1
[HttpGet("Queried")]
public LearningResource Queried(string listName)
{
 return _sampleRepository.GetByListName(listName);
}

While the application is running, navigate to the following endpoint:

  • https://localhost:44350/api/LearningResources/Queried?listName=RL1
Sample JSON data with QueryString parameter
Sample JSON data with QueryString parameter

This triggers a GET request by calling the LearningResourcesController‘s Queried() method, with the listName parameter. When passing the list name “RL1”, this returns one item, as shown below:

{
    "id": 1,
    "name": "ASP .NET Core Docs",
    "url": "https://docs.microsoft.com/aspnet/core",
    "resourceListId": 1,
    "resourceList": {
        "id": 1,
        "name": "RL1",
        "learningResources": []
    },
    "contentFeedUrl": null,
    "learningResourceTopicTags": null
}

As you can see, the above result is in JSON format for the returned object.

XML Output

Wait a minute… with all these JSON results, when will we get to XML output? Not to worry, there are multiple ways to get XML results while reusing the above code. First, update your Startup.cs file’s ConfigureServices() to include a call to services.AddControllers().AddXmlSeralizerFormatters():

public void ConfigureServices(IServiceCollection services)
{
   ...
   services.AddControllers()
    .AddXmlSerializerFormatters();
   ...
}

In Postman, set the request’s Accept header value to “application/xml” before requesting the endpoint, then run the application and navigate to the following endpoint once again:

  • https://localhost:44350/api/LearningResources/RL1
XML-formatted results in Postman without code changes
XML-formatted results in Postman without code changes

This should provide the following XML results:

<LearningResource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Id>1</Id>
    <Name>ASP .NET Core Docs</Name>
    <Url>https://docs.microsoft.com/aspnet/core</Url>
    <ResourceListId>1</ResourceListId>
    <ResourceList>
        <Id>1</Id>
        <Name>RL1</Name>
        <LearningResources />
    </ResourceList>
</LearningResource>

Since the action method returns a complex object, the result can easily be switched to XML simply by changing the Accept header value. In order to return XML using an IActionResult method, you should also use the [Produces] attribute, which can be set to “application/xml” at the API Controller level.

[Produces("application/xml")]
[Route("api/[controller]")]
[ApiController]
public class LearningResourcesController : ControllerBase
{
   ...
}

Then revisit the following endpoint, calling the search action method with the fragment parameter set to “ir”:

  • https://localhost:44350/api/LearningResources/Queried?listName=RL1

At this point, it is no longer necessary to set the Accept header to “application/xml” (in Postman) during the request, since the [Produces] attribute is given priority over it.

XML-formatted output using Produces attribute
XML-formatted output using Produces attribute

This should produces the following result , with a LearningResource object in XML:

<LearningResource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Id>1</Id>
    <Name>ASP .NET Core Docs</Name>
    <Url>https://docs.microsoft.com/aspnet/core</Url>
    <ResourceListId>1</ResourceListId>
    <ResourceList>
        <Id>1</Id>
        <Name>RL1</Name>
        <LearningResources />
    </ResourceList>
</LearningResource>

As for the first Get() method returning JsonResult, you can’t override it with the [Produces] attribute or the Accept header value to change the result to XML format.

To recap, the order of precedence is as follows:

  1. public JsonResult Get()
  2. [Produces(“application/…”)]
  3. Accept: “application/…”

References

XML + JSON Serialization in ASP .NET Core

By Shahed C on June 17, 2019

This is the twenty-fourth of a series of posts on ASP .NET Core in 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

In this Article:

X is for XML + JSON Serialization

XML (eXtensible Markup Language) is a popular document format that has been used for a variety of applications over the years, including Microsoft Office documents, SOAP Web Services, application configuration and more. JSON (JavaScript Object Notation) was derived from JavaScript, but has also been used for storing data in both structured and unstructured formats, regardless of language used. In fact, ASP .NET Core applications switched from XML-based .config files to JSON-based .json settings files for application configuration.

XmlJsonSerialization

This article will refer to the following sample code on GitHub, derived from the guidance provided in the official documentation + sample:

Web XML + JSON Serialization : https://github.com/shahedc/XmlJsonSerialization

Returning JsonResult and IActionResult

Before we get into XML serialization, let’s start off with JSON Serialization first, and then we’ll get to XML. If you run the Web API sample project for this blog post, you’ll notice a CIController.cs file that represents a “Cinematic Item Controller” that expose API endpoints. These endpoints can serve up both JSON and XML results of Cinematic Items, i.e. movies, shows and shorts in a Cinematic Universe.

Run the application and navigate to the following endpoint in an API testing tool, e.g. Postman:

  • https://localhost:44372/api/ci/

Serialization-Get

This triggers a GET request by calling the CIController‘s Get() method:

  // GET: api/ci
 [HttpGet]
 public JsonResult Get()
 {
    return Json(_cinematicItemRepository.CinematicItems());
 }

In this case, the Json() method returns a JsonResult object that serializes a list of Cinematic Items. For simplicity, the _cinematicRepository object’s CinematicItems() method (in CinematicItemRepository.cs) returns a hard-coded list of CinematicItem objects. Its implementation here isn’t important, because you would typically retrieve such values from a persistent data store, preferably through some sort of service class.

public List<CinematicItem> CinematicItems()
{
   return new List<CinematicItem>
   {
      new CinematicItem
      {
         Title = "Iron Man 1",
         Description = "First movie to kick off the MCU.",
         Rating = "PG-13",
         ShortName = "IM1",
         Sequel = "IM2"
      },
      ... 
   }
}

The JSON result looks like the following, where a list of movies are returned:

[
 {
 "title": "Avengers: Age of Ultron",
 "description": "2nd Avengers movie",
 "rating": "PG-13",
 "shortName": "AV2",
 "sequel": "AV3"
 },
 {
 "title": "Avengers: Endgame",
 "description": "4th Avengers movie",
 "rating": "PG-13",
 "shortName": "AV4",
 "sequel": ""
 },
 {
 "title": "Avengers: Infinity War",
 "description": "3rd Avengers movie",
 "rating": "PG-13",
 "shortName": "AV3",
 "sequel": "AV4"
 },
 {
 "title": "Iron Man 1",
 "description": "First movie to kick off the MCU.",
 "rating": "PG-13",
 "shortName": "IM1",
 "sequel": "IM2"
 },
 {
 "title": "Iron Man 2",
 "description": "Sequel to the first Iron Man movie.",
 "rating": "PG-13",
 "shortName": "IM2",
 "sequel": "IM3"
 },
 {
 "title": "Iron Man 3",
 "description": "Wraps up the Iron Man trilogy.",
 "rating": "PG-13",
 "shortName": "IM3",
 "sequel": ""
 },
 {
 "title": "The Avengers",
 "description": "End of MCU Phase 1",
 "rating": "PG-13",
 "shortName": "AV1",
 "sequel": "AV2"
 }
]

Instead of specifically returning a JsonResult, you could also return a more generic IActionResult, which can still be interpreted as JSON. Run the application and navigate to the following endpoint, to include the action method “search” folllowed by a QueryString parameter “fragment” for a partial match.

  • https://localhost:44372/api/ci/search?fragment=ir

Serialization-Get-Search

This triggers a GET request by calling the CIController‘s Search() method, with its fragment parameter set to “ir” for a partial text search:

// GET: api/ci/search?fragment=ir
[HttpGet("Search")]
public IActionResult Search(string fragment)
{
   var result = _cinematicItemRepository.GetByPartialName(fragment);
   if (!result.Any())
   {
      return NotFound(fragment);
   }
   return Ok(result);
}

In this case, the GetByPartialName() method returns a List of CinematicItem objects that are returned as JSON by default, with a HTTP 200 OK status. In case no results are found, the action method will return a 404 with the NotFound() method.

public List<CinematicItem> GetByPartialName(string titleFragment)
{
   return CinematicItems()
      .Where(ci => ci.Title
         .IndexOf(titleFragment, 0, StringComparison.CurrentCultureIgnoreCase) != -1)
      .ToList();
}

The JSON result looks like the following, where any movie title partially matches the string fragment provided:

[
 {
 "title": "Iron Man 1",
 "description": "First movie to kick off the MCU.",
 "rating": "PG-13",
 "shortName": "IM1",
 "sequel": "IM2"
 },
 {
 "title": "Iron Man 2",
 "description": "Sequel to the first Iron Man movie.",
 "rating": "PG-13",
 "shortName": "IM2",
 "sequel": "IM3"
 },
 {
 "title": "Iron Man 3",
 "description": "Wraps up the Iron Man trilogy.",
 "rating": "PG-13",
 "shortName": "IM3",
 "sequel": ""
 }
]

Returning Complex Objects

An overloaded version of the Get() method takes in a “shortName” string parameter to filter results by an alternate short name for each movie in the repository for the cinematic universe. Instead of returning a JsonResult or IActionResult, this one returns a complex object (CinematicItem) that contains properties that we’re interested in.

// GET api/ci/IM1
[HttpGet("{shortName}")]
public CinematicItem Get(string shortName)
{
   return _cinematicItemRepository.GetByShortName(shortName);
}

The GetByShortName() method in the CinematicItemRepository.cs class simply checks for a movie by the shortName parameter and returns the first match. Again, the implementation is not particularly important, but it illustrates how you can pass in parameters to get back JSON results.

public CinematicItem GetByShortName(string shortName)
{
   return CinematicItems().FirstOrDefault(ci => ci.ShortName == shortName);
}

While the application is running, navigate to the following endpoint:

  • https://localhost:44372/api/ci/IM1

Serialization-Get-ShortName

This triggers another GET request by calling the CIController‘s overloaded Get() method, with the shortName parameter. When passing the short name “IM1”, this returns one item  “Iron Man 1”, as shown below:

{
   "title": "Iron Man 1",
   "description": "First movie to kick off the MCU.",
   "rating": "PG-13",
   "shortName": "IM1",
   "sequel": "IM2"
}

Another example with a complex result takes in a parameter via QueryString and checks for an exact match with a specific property. In this case the Related() action method calls the repository’s GetBySequel() method to find a specific movie by its sequel’s short name.

// GET: api/ci/related?sequel=IM2
[HttpGet("Related")]
public CinematicItem Related(string sequel)
{
 return _cinematicItemRepository.GetBySequel(sequel);
}

The GetBySequel() method in the CinematicItemRepository.cs class  checks for a movie’s sequel by the shortName parameter and returns the first match.

public CinematicItem GetBySequel(string sequelShortName)
{
   return CinematicItems().FirstOrDefault(ci => ci.Sequel == sequelShortName);
}

While the application is running, navigate to the following endpoint:

  • https://localhost:44372/api/ci/related?sequel=IM3

Serialization-Get-Sequel

This triggers a GET request by calling the CIController‘s Related() method, with the sequel parameter. When passing the sequel’s short name “IM3”, this returns one item “Iron Man 2”, as shown below:

{
   "title": "Iron Man 2",
   "description": "Sequel to the first Iron Man movie.",
   "rating": "PG-13",
   "shortName": "IM2",
   "sequel": "IM3"
}

As you can see, the result is in JSON format for the returned object.

XML Serialization

Wait a minute… with all these JSON results, when will we get to XML serialization? Not to worry, there are multiple ways to get XML results while reusing the above code. First, add the NuGet package “Microsoft.AspNetCore.Mvc.Formatters.Xml” to your project and then update your Startup.cs file’s ConfigureServices() to include a call to services.AddMvc.AddXmlSeralizerFormatters():

public void ConfigureServices(IServiceCollection services)
{
   ...
   services.AddMvc()
    .AddXmlSerializerFormatters();
   ...
}

Set the request’s Accept header value to “application/xml” before requesting the endpoint, then run the application and navigate to the following endpoint once again:

  • https://localhost:44372/api/ci/IM1

Serialization-Get-XML

This should provide the following XML results:

<CinematicItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <Title>Iron Man 1</Title>
 <Description>First movie to kick off the MCU.</Description>
 <Rating>PG-13</Rating>
 <ShortName>IM1</ShortName>
 <Sequel>IM2</Sequel>
</CinematicItem>

Since the action method returns a complex object, the result can easily be switched to XML simply by changing the Accept header value. In order to return XML using an IActionResult method, you should also use the [Produces] attribute, which can be set to “application/xml” at the Controller level

[Produces("application/xml")]
[Route("api/[controller]")]
[ApiController]
public class CIController : Controller
{
   ...
}

Then revisit the following endpoint, calling the search action method with the fragment parameter set to “ir”:

  • https://localhost:44372/api/ci/search?fragment=ir

At this point, it is no longer necessary to set the Accept header to “application/xml” during the request, since the [Produces] attribute is given priority over it.

Serialization-Get-Search-XML

This should produces the following result , with an array of CinematicItem objects in XML:

<ArrayOfCinematicItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <CinematicItem>
 <Title>Iron Man 1</Title>
 <Description>First movie to kick off the MCU.</Description>
 <Rating>PG-13</Rating>
 <ShortName>IM1</ShortName>
 <Sequel>IM2</Sequel>
 </CinematicItem>
 <CinematicItem>
 <Title>Iron Man 2</Title>
 <Description>Sequel to the first Iron Man movie.</Description>
 <Rating>PG-13</Rating>
 <ShortName>IM2</ShortName>
 <Sequel>IM3</Sequel>
 </CinematicItem>
 <CinematicItem>
 <Title>Iron Man 3</Title>
 <Description>Wraps up the Iron Man trilogy.</Description>
 <Rating>PG-13</Rating>
 <ShortName>IM3</ShortName>
 <Sequel />
 </CinematicItem>
</ArrayOfCinematicItem>

As for the first Get() method returning JsonResult, you can’t override it with the [Produces] attribute or the Accept header value to change the result to XML format.

To recap, the order of precedence is as follows:

  1. public JsonResult Get()
  2. [Produces(“application/…”)]
  3. Accept: “application/…”

References