{"id":2740,"date":"2015-01-05T18:06:00","date_gmt":"2015-01-05T18:06:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/odatateam\/2015\/01\/05\/tutorial-sample-odatalib-custom-payload-format\/"},"modified":"2024-02-16T15:06:04","modified_gmt":"2024-02-16T22:06:04","slug":"tutorial-sample-odatalib-custom-payload-format","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/odata\/tutorial-sample-odatalib-custom-payload-format\/","title":{"rendered":"[Tutorial &amp; Sample] ODataLib Custom Payload Format"},"content":{"rendered":"<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">This post is to guide you through the custom payload format support in ODataLib, which was introduced in ODataLib 6.9.0 release.<\/span><\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">All the sample codes in this post have been put to <a href=\"https:\/\/github.com\/OData\/ODataSamples\">ODataSamples project<\/a>, you can check it out under ScenariosCustomFormat folder.<\/span><\/span><\/p>\n<h2><span style=\"color: #2e74b5;\"><span style=\"font-family: Calibri Light;\">Background<\/span><\/span><\/h2>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">The OData specification defined several payload formats for data exchange. ODataLib has got built-in support for the JSON format(see <a href=\"https:\/\/docs.oasis-open.org\/odata\/odata-json-format\/v4.0\/odata-json-format-v4.0.html\">OData JSON Format spec<\/a>). But there are cases when someone tries to build up a RESTful service following OData conventions, and also wants to use a custom payload format other than JSON. For example, assuming we have an existing service that uses a custom data format understood by existing clients.\u00a0 If we change the service to OData, we may prefer to keep the current data format, so that while taking advantage of OData features, existing clients could still consume the data returned by the service, or generate request that the service could read.<\/span><\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">The OData library was designed to support various payload formats, however, some of those read\/write APIs were not publicly visible. In 6.9.0 release, we changed some of those APIs to be public, so that users are able to write out payloads with custom format.<\/span><\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">In the following section, we\u2019ll first start by looking at the overall architecture of ODataLib\u2019s reader\/writer component and the media type resolving process. Then I will give a demo on how to write a custom payload extension for CSV (comma separated value) format.<\/span><\/span><\/p>\n<h2><span style=\"color: #2e74b5;\"><span style=\"font-family: Calibri Light;\">Reader\/Writer Overview<\/span><\/span><\/h2>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">Here is a figure to describe the main classes used by ODataLib\u2019s reader and writer component. <\/span><\/span><\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2015\/01\/5270.iou_.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/odatateam\/wp-content\/uploads\/sites\/23\/2015\/01\/5270.iou_.png\" alt=\"\" border=\"0\" \/><\/a><\/p>\n<p>[click to enlarge]<\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">As you can see, the ODataLib\u2019s reader and writer share similar structure. The main entry point classes are ODataMessageReader and ODataMessageWriter. They both take a message and a setting class as input. Then when user calls some read\/write APIs, ODataMessageReader\/ODataMessageWriter would internally figure out the proper payload format to use, then perform corresponding input\/output actions using that format.<\/span><\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">Let\u2019s take a look at what happens when user tries to write an entry in response payload:<\/span><\/span><\/p>\n<ol>\n<li><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">User prepares an OData response message, sets the header information, and then creates an ODataMessageWriter;<\/span><\/span><\/li>\n<li><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">User calls CreateODataEntryWriter method on ODataMessageWriter, and gets a format specific ODataWriter;<\/span><\/span><\/li>\n<li><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">User calls writing actions on the ODataWriter, such as StartEntry, EndEntry, etc.<\/span><\/span><\/li>\n<\/ol>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">In step 2, when creating the format specific ODataWriter, ODataMessageWriter would at first figure out what the payload format to use, we call this media type resolving. After that, an ODataFormat instance is present. Later on the ODataMessageWriter calls the CreateOutputContext method on ODataFormat to get the format specific output context, then calls the corresponding method on the context (CreateEntryWriter in this case).<\/span><\/span><\/p>\n<h2><span style=\"color: #2e74b5;\"><span style=\"font-family: Calibri Light;\">Media Type Resolving<\/span><\/span><\/h2>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">For media type resolving, the input is the content-type header from ODataMessage, or instructions from settings (for writing only), and the output is an ODataFormat class. The ODataFormat represents the actual payload format, and it is the key point for decoupling various formats with reader\/writer APIs. Besides, we have ODataMediaType class which represents a certain kind of media type. Also, we have got ODataMediaTypeFormat that gets one ODataFormat bound to an ODataMediaType. At last, we provide MediaTypeResolver class responsible for choosing correct media types.<\/span><\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">MediaTypeResolver class contains following method:<\/span><\/span><\/p>\n<p><strong>public virtual IEnumerable&lt;ODataMediaTypeFormat&gt; GetMediaTypeFormats(ODataPayloadKind payloadKind)<\/strong><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">This API should give out which media types are available for given payload kind. Internally, ODataLib\u2019s reader and writer would first call this method for certain payload kind to get a list of supported ODataMediaTypeFormat, then it would choose the best match based on media type information from request message.<\/span><\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">In general, the default implementation of MediaTypeResolver would return JSON format for data request, and XML format for metadata request. Derived classes could choose to override this method to have their own behavior. Thus users could provide a custom media payload format by overriding it and returning the expected ODataMeidaFormat. We\u2019ll demo how to write a custom MediaTypeResolver in following section.<\/span><\/span><\/p>\n<h2><span style=\"color: #2e74b5;\"><span style=\"font-family: Calibri Light;\">Implementing a CSV Format extension<\/span><\/span><\/h2>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">Here we\u2019ll have a demo on how to implement an extension that supports writing out CSV format payload. Our goal is to support writing an ODataEntry as a CSV format, every property appear to be in a single column. Please make sure your project have installed\u00a0<a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.OData.Core\/\">Microsoft.OData.Core 6.9.0<\/a> package from NuGet gallery before getting started.<\/span><\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">At first, we will implement the class CsvWriter and CsvOutputContext.<\/span><\/span><\/p>\n<ul>\n<li><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">The class CsvWriter derives from ODataWriter, to simplify the demo, we omitted some validation logic and the async API implementation here.<\/span><\/span><\/li>\n<li><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">The class CsvOutputContext derived from ODataOutputContext, this class acts as a bridge between ODataMessageWriter and specific writer, we&#8217;ll return CsvWriter instances from CreateODataEntryWriter\/CreateODataFeedWriter method here.<\/span><\/span><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p><script type=\"text\/javascript\" src=\"https:\/\/gist.github.com\/ODataTeam\/668d9fa182c3f74f012b.js\"><\/script>\n<script type=\"text\/javascript\" src=\"https:\/\/gist.github.com\/ODataTeam\/4d6b8b682c1f392bda5f.js\"><\/script><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">Then we can write our own CsvFormat class, which is quite simple. Please note that we omit implementation for other abstract method here.<\/span><\/span><\/p>\n<p><script type=\"text\/javascript\" src=\"https:\/\/gist.github.com\/ODataTeam\/cd7beceddfd846a4b386.js\"><\/script><\/p>\n<p><span style=\"font-family: Calibri; font-size: small;\">\u00a0<\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">At last, we can implement the MediaTypeResolver for CSV, here we\u2019ll bind media type \u2018text\/csv\u2019 to our CsvFormat.<\/span><\/span><\/p>\n<p><script type=\"text\/javascript\" src=\"https:\/\/gist.github.com\/ODataTeam\/c006ff134167c04e7275.js\"><\/script><\/p>\n<p><span style=\"font-family: Calibri; font-size: small;\">\u00a0<\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">Here is a usage demo for writing an OData feed with CSV format. Please note that we almost use same code logic for writing JSON payload here. The only difference is we\u2019ll set our CsvMediaTypeResolver to the MessageWriterSettings, while the message\u2019s content type header is set to CSV format.<\/span><\/span><\/p>\n<p><script type=\"text\/javascript\" src=\"https:\/\/gist.github.com\/ODataTeam\/88f325a3f622eaf1205e.js\"><\/script><\/p>\n<p><span style=\"font-family: Calibri; font-size: small;\">\u00a0<\/span><\/p>\n<p><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">Here is the output:<\/span><\/span><\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"623\"><strong><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">Id,Name,<\/span><\/span><\/strong><\/p>\n<p><strong><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">51,Name_A,<\/span><\/span><\/strong><\/p>\n<p><strong><span style=\"font-size: small;\"><span style=\"font-family: Calibri;\">52,Name_B,<\/span><\/span><\/strong><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><span style=\"font-family: Calibri; font-size: small;\">\u00a0<\/span><\/p>\n<p><span style=\"font-family: Calibri; font-size: small;\">In our ODataSample project, we have a WebAPI service project which supports both CSV and VCard formats, you can check it out <a href=\"https:\/\/github.com\/OData\/ODataSamples\/tree\/master\/Scenarios\/CustomFormat\">here<\/a>.<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is to guide you through the custom payload format support in ODataLib, which was introduced in ODataLib 6.9.0 release. All the sample codes in this post have been put to ODataSamples project, you can check it out under ScenariosCustomFormat folder. Background The OData specification defined several payload formats for data exchange. ODataLib has [&hellip;]<\/p>\n","protected":false},"author":516,"featured_media":3253,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2740","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-odata"],"acf":[],"blog_post_summary":"<p>This post is to guide you through the custom payload format support in ODataLib, which was introduced in ODataLib 6.9.0 release. All the sample codes in this post have been put to ODataSamples project, you can check it out under ScenariosCustomFormat folder. Background The OData specification defined several payload formats for data exchange. ODataLib has [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/2740","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/users\/516"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/comments?post=2740"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/2740\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media\/3253"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media?parent=2740"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/categories?post=2740"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/tags?post=2740"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}