{"id":2155,"date":"2013-11-15T08:02:53","date_gmt":"2013-11-14T20:02:53","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2155"},"modified":"2013-11-15T08:12:20","modified_gmt":"2013-11-14T20:12:20","slug":"bonjour-json","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2155\/","title":{"rendered":"Bonjour JSON"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>I know, you wait 5 years for a library then three come along at once!  As well as <strong>Smoketest<\/strong> I also want to mention a couple of other libraries that have been published alongside it.  They are wholly unrelated to Smoketest itself, so I decided to just quickly mention them in this separate post.<\/p>\n<p><!--more--><\/p>\n<p>The first thing to mention is that as with all my code, these libraries support <strong>all<\/strong> versions of Delphi back to <strong>Delphi 7<\/strong>.  It is only tested on Win32 VCL platforms (and Win64 where supported).<\/p>\n<h2>JSON<\/h2>\n<p>The first is a <strong>JSON<\/strong> implementation and parser that I wrote a long, long time ago though it has been updated a little recently.  This is incorporated in <a href=\"https:\/\/github.com\/deltics\/delphi.libs\/blob\/master\/rtl\/Deltics.JSON.pas\" target=\"_blank\">a single unit in the <code>rtl<\/code> library of the <code>delphi.libs<\/code> project<\/a> that also contains <strong>Smoketest<\/strong>.<\/p>\n<p>One thing to note about this implementation is that all non-ASCII values are emitted as <strong>escaped Unicode<\/strong> (<code>\\Uxxxx<\/code>).<\/p>\n<p>Duplicate value names are supported, taking a li<del>b<\/del>teral approach to the interpretation of the specification (<a href=\"http:\/\/www.ietf.org\/rfc\/rfc4627.txt\">RFC-4267<\/a>).  The specification states that duplicate names <strong>SHOULD<\/strong> be supported but does not stipulate <strong>how<\/strong>.<\/p>\n<p>My JSON parser supports duplicate values currently by simply creating a JSON structure with all named values.  Where duplicate names exist in a JSON structure only the first will be found if accessed by name, so given the JSON data:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n  {\r\n    &quot;duplicate&quot;: &quot;first&quot;,\r\n    &quot;duplicate&quot;: &quot;second&quot;\r\n  }\r\n<\/pre>\n<p>Accessing the &#8220;<strong>duplicate<\/strong>&#8221; value by name (the default property of a JSON object):<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  str := json['duplicated'].AsString;  \/\/ returns 'first'\r\n<\/pre>\n<p>But <strong>all<\/strong> occurrences of a particular named value may be accessed by ordinal index just as with any other value:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  str := json.ValueByIndex[0].AsString;  \/\/ returns 'first'\r\n  str := json.ValueByIndex[1].AsString;  \/\/ returns 'second'\r\n<\/pre>\n<p>Similarly, if you explicitly add two values with the same name then that is what the JSON object will emit.  If you change a value by name then you will replace the value of the first such named value, but again you can still change any value by ordinal index:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  json.Add('duplicate', 'first');\r\n  json.Add('duplicate', 'second');\r\n\r\n  json['duplicate'].AsString    := 'new first';\r\n  json.ValueByIndex[1].AsString := 'new second';\r\n\r\n\r\n\/\/ result:\r\n\/\/\r\n\/\/   {\r\n\/\/    &quot;duplicate&quot;: &quot;new first&quot;,\r\n\/\/    &quot;duplicate&quot;: &quot;new second&quot;\r\n\/\/   }\r\n<\/pre>\n<p>In effect this means that the JSON object itself makes no decision about how to treat duplicate names where they exist, leaving it for the consumer code to make this decision and behave accordingly.<\/p>\n<p>On that note I would add that the ambiguity of the specification in this area is a well known problem and I strongly recommend that in any JSON implementation you avoid any potential problems by simply not using duplicated value names at all.  But if you are presented with JSON that does, at least this implementation allows you to choose how to work with them.<\/p>\n<p>I am currently contemplating providing a mechanism to specify how the parser should treat duplicate names so that application code can delegate any particular implementation decision in this area to the JSON object.<\/p>\n<p>A number of non-JSON data types are supporting on the API (GUID, TDateTime, enums etc), but these all resolve down to JSON core types underneath &#8211; usually <strong>string<\/strong> &#8211; and are simply provided as a convenience when using JSON as an interchange data structure being passed between two Delphi authored processes.<\/p>\n<h2>BONJOUR<\/h2>\n<p>The second is a far more sophisticated affair:  A set of components to support the <a href=\"https:\/\/developer.apple.com\/bonjour\/\">Apple Bonjour Service Discovery API<\/a>.<\/p>\n<p>This is also in the <code>delphi.libs<\/code> project but is <a href=\"https:\/\/github.com\/deltics\/delphi.libs\/tree\/master\/bonjour\" target=\"_blank\">a library in it&#8217;s own right<\/a>.<\/p>\n<p>The components are intended primarily for the very simple advertisement and discovery of services, rather than being a complete <strong>DNS-SD<\/strong> framework (whatever that means &#8211; something I am still learning on an as-needed basis).<\/p>\n<p>To advertise a service you use a <strong>TService<\/strong> component, simply set the service <strong>name<\/strong>, <strong>type<\/strong> and <strong>port<\/strong> and set it active.  So for example, to advertise a (fake) <strong>iTunes shared library<\/strong>:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  service := TService.Create(self);\r\n  service.ServiceName := 'A Fake iTunes Library';\r\n  service.ServiceType := '_daap._tcp';  \/\/ the service type of an iTunes library\r\n  service.Port        := 3572;\r\n  service.Active      := TRUE;\r\n<\/pre>\n<p>You can also add <strong>TXT<\/strong> records for your advertised service if you need to.<\/p>\n<p>Discovering services is a little bit more involved, though not much.  For this you use a <strong>TListener<\/strong>.  Set the service type you are interested in, assign a handler to respond to newly discovered services (and services that disappear, if you need to).  When you discover a service you typically resolve it (in your <strong>ServiceFound<\/strong> handler) to obtain further information about the service so you will need a handler for the event that resolving a service will trigger.  Then set the listener active:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure TMyForm.DoServiceFound(const aSender: TListener; \r\n                                   const aService: TServiceInfo);\r\n  begin\r\n    aService.Resolve;\r\n  end;\r\n\r\n\r\n  procedure TMyForm.DoServiceResolved(const aSender: TListener;\r\n                                      const aService: TServiceInfo);\r\n  begin\r\n    \/\/ service hostname, port and TXT records are now available...\r\n  end;\r\n\r\n\r\n  listener := TListener.Create(self);\r\n  listener.ServiceType       := '_daap._tcp';\r\n  listener.OnServiceFound    := DoServiceFound;\r\n  listener.OnServiceResolved := DoServiceResolved;\r\n  listener.Active            := TRUE;\r\n<\/pre>\n<p>Alternatively you can resolve a service synchronously by specifying a timeout (in milliseconds) to the <code>Resolve<\/code> method, avoiding the need for a second event handler.  In this case, the <code>Resolve<\/code> method returns a <strong>boolean<\/strong> indicating whether the service was resolved within the time allowed (if it didn&#8217;t it may still eventually resolve):<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure TMyForm.DoServiceFound(const aSender: TListener; \r\n                                   const aService: TServiceInfo);\r\n  begin\r\n    if aService.Resolve(1000) then\r\n      \/\/ service resolved - host, port and TXT etc now available \r\n  end;\r\n<\/pre>\n<p>I hope to find time to blog more about these libraries in the coming weeks, but if you&#8217;re prepared to get your hands filthy and learn the hard way (not least by finding my bugs for me &#8211; thanks Stefan! :)) then the code is already up in <a href=\"https:\/\/github.com\/deltics\/delphi.libs\">the same github repository as Smoketest<\/a>.<\/p>\n<p>Both libraries have <strong>Smoketest<\/strong> test projects.<\/p>\n<p>These are far from complete and are frankly more than a little scruffy, but should provide some initial guidance on how to use these libraries, if you are interested.<\/p>\n<p>Once again, both of these libraries support <strong>all<\/strong> versions of Delphi back to <strong>Delphi 7<\/strong>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> I know, you wait 5 years for a library then three come along at once! As well as Smoketest I also want to mention a couple of other libraries that have been published alongside it. They are wholly unrelated to Smoketest itself, so I decided to just quickly mention them in this separate post.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[259,292,260],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-yL","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2169,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2169\/","url_meta":{"origin":2155,"position":0},"title":"Smoketest &#8211; Set Me Up \/ Tear Me Down","date":"20 Nov 2013","format":false,"excerpt":"In a previous post I demonstrated how the default \"pretty name\" for a Smoketest test case (derived from the test case classname) can be over-ridden by a test developer by implementing a specific interface (INameCase) on the test case class itself. There are some other interfaces that can be implemented\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2095,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2095\/","url_meta":{"origin":2155,"position":1},"title":"Extending Smoketest (Part 1) &#8211; An Inspector Calls","date":"05 Nov 2013","format":false,"excerpt":"In the soon to be released Smoketest framework it is sometimes useful to create new test types to supplement the tests built-in to the framework. In this and the next post I will walk through the process of implementing and registering a custom test with the Smoketest framework. In a\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2006,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2006\/","url_meta":{"origin":2155,"position":2},"title":"Blowing Smoke&#8230;","date":"01 Nov 2013","format":false,"excerpt":"To alleviate the grind of polishing and sanitising my code (and, let's be honest, just plain 'fixing' it in some cases) ready for release, I have re-kindled my participation on Stack Overflow. In a happy confluence yesterday a question came up which allowed me to exercise one of the libraries\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"Ready to Run","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2193,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2193\/","url_meta":{"origin":2155,"position":3},"title":"DUnit Compatibility in Smoketest","date":"25 Nov 2013","format":false,"excerpt":"To address some odd concerns about differences between DUnit and Smoketest, I thought it would be useful to demonstrate how it is entirely within the gift of a Smoketest user to create their own \"comfort\" layer, to make using Smoketest more similar to the DUnit framework if they wish (though\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":284,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/284\/","url_meta":{"origin":2155,"position":4},"title":"This Week&#8217;s Poll, Delphi at Tech Ed and 2009 Ship Date","date":"01 Sep 2008","format":false,"excerpt":"Since I'm preparing a series of posts about (and eventual publication of) Smoketest, my own testing framework, I thought I'd test the water with this weeks poll and see what - if anything - people are already using as far as unit testing goes. Also I thought I'd briefly mention\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2111,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2111\/","url_meta":{"origin":2155,"position":5},"title":"Extending Smoketest (Part 2) &#8211; Great(er) Expectations","date":"07 Nov 2013","format":false,"excerpt":"In my previous post on Smoketest I showed how you can extend the inspections framework to work with complex types in your code. As promised, I shall now show how you can do much the same thing to extend the framework with entirely new tests. \u201cI must be taken as\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2155"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=2155"}],"version-history":[{"count":7,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2155\/revisions"}],"predecessor-version":[{"id":2161,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2155\/revisions\/2161"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2155"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2155"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2155"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}