22

The answer of Get OData $metadata in JSON format states that OData cannot return the metadata as JSON by default.

But is it possible then to capture or hook its response for the $metadata URL, and then convert it on the fly to JSON before sending it to the client?

I imagine pseudocode like this:

[HttpGet]
[ODataRoute("$metadata")]
public string GetMetadataAsJson()
{
    string xml = GetOdataMetadataAsXML();
    string json = ConvertToJson(xml);
    return json;
}

I don't know how to implement it correctly, though, in particular I'm not sure how to get the standard OData response as a string, and how to hook the $metadata URL.

sashoalm
  • 75,001
  • 122
  • 434
  • 781
  • One option would be to write a HttpModule. Hook in on the expected URL, incoming change request type from app/json to app/xml, outcoming replace the response stream with a XDoc.Load->JSon.ConvertObject->Output stream. – Marvin Smit Dec 17 '16 at 09:14
  • @MarvinSmit I'm having trouble even hooking the `$metadata` endpoint, `[ODataRoute("$metadata")]` didn't work for that. The problem is that `$metadata` is more like an argument that can be applied to many different URLs, and I'd have to hook all of them. – sashoalm Dec 19 '16 at 16:04
  • I wasn't suggesting doing it inside your existing code. But more like "pre-post" processing in a custom HttpModule. Incoming you check the URL for EndsWith("$metadata") and request type application/json. If so, change app/json to app/xml and let OData do its normal XML version. Then on the way out, read that XML, JSON.Convert and output the JSON instead. – Marvin Smit Dec 20 '16 at 09:42
  • @MarvinSmit OK. Since I'm new to ASP.NET, by Custom HTTP Module you mean that, right - https://msdn.microsoft.com/en-us/library/ms227673.aspx? – sashoalm Dec 20 '16 at 09:50
  • Yes. Hook in postAuth, check the incoming request for the expected headers/url pieces. change headers to have the normal $metadata functionality kick in (outputs XML). Hook in post request handler, Then read the 'output" as xml and convert to JSON. Then output your JSON instead of the original XML. – Marvin Smit Dec 20 '16 at 15:13
  • 1
    The more complex option is to: Write a decorator for the ODataMediaTypeFormatter responsible for the $metadata generation. Then use some reflection to have the OData routing allowing app/json for $metadata request (by default it fails the route). Then overload the "WriteToStreamAsyncProxy" on your decorator to to do same trick as mentioned above (write XML, convert XML to JSON, output JSON). And finally replace the formatters in your OData startup with decorated versions. – Marvin Smit Dec 20 '16 at 15:19
  • 4
    I don't understand what's the usage for such a thing? There is no possible equivalence between the CSDL xml and json (what would that json like? what would you do with XML namespaces, for example?). No standard client will be able to understand it anyway, so, why don't you just create another specific route like /$jsonmetadata or whatever and use that route in your custom client? – Simon Mourier Dec 21 '16 at 08:54
  • XML is more structured than JSON so its very easy to write a client-side interpreter to convert it into JSON on the client side. It's only a few lines in javascript, C# or java, so there is little value is doing this on the API side. – Chris Schaller Jul 07 '20 at 03:50
  • 1
    This still bothers me ;) You _shouldn't_ try to intercept the standard _conventional_ $metadata route, this would break OData client tools, but nothing is stopping you from creating your own un-bound action to achieve the save thing. – Chris Schaller Mar 19 '21 at 01:23

1 Answers1

1

Newtonsoft supports the Json part checkout https://www.newtonsoft.com/json/help/html/ConvertXmlToJson.htm

So the actual solution for the Json Part would be quite simple, as soon as you have your XML

[HttpGet]
[ODataRoute("$metadata")]
public string GetMetadataAsJson()
{
    string xml = GetOdataMetadataAsXML();
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xml);
    string json = JsonConvert.SerializeXmlNode(doc);
    return json;
}

Also you should probably first check if e.g. a format query is set for example with this code

[HttpGet]
[ODataRoute("$metadata")]
public string GetMetadataAsJson([FromQuery(Name="Format")]string format)
{
    string metaResult = GetOdataMetadataAsXML();
    if(format.Equals("json",StringComparison.OrdinalIgnoreCase))
    {
        XmlDocument metaDoc = new XmlDocument();
        doc.LoadXml(metaResult);
        metaResult = JsonConvert.SerializeXmlNode(doc);
    }
    return metaResult;
}
Fabian Kamp
  • 129
  • 10