# Monday, November 24, 2008

What is JSON?
If you are unfamiliar with JSON, you can dig into the particulars about it here and here. The short explanation is that JSON provides a format for serializing and deserializing variables, arrays, and class instances. For those of you that may not know what serializing and deserializing are, think of serializing as turning a cursor into XML and then think of deserializing as turning XML back into a cursor. Basically the process of turning an instance of MySomething into a string and then turning that string back into an instance of the same MySomething.

Now, besides the obvious advantages of being able to serialize and deserialize VFP values and objects (persisting them on disk or passing them across the wire, just to name a couple), it becomes really useful when you find that you have to interact with Javascript from your Visual FoxPro application. I ran into this while working with Virtual Earth in a webbrowser control (more on that in an upcoming blog entry). I wanted to use JSON as part of my solution because it would allow me to easily move values, instances, and data back and forth between Visual FoxPro and the Javascript that was running inside a page being displayed in the webbrowser control. I could have done without it, but it's a lot more slick when using JSON.

JSON for VFP
If you visited the first "here" in my first paragraph in the section above you may have noted that they have a rather impressive list of languages that provide support, in some manner, for JSON at the bottom of the page. You may have also noted that Visual FoxPro was absent from that list. Now, that doesn't mean that no one has ever done this sort of thing before. In fact, Rick Strahl provides VFP/JSON support through his wwJSONSerializer class and FoxWeb provides similar functionality through it's fwJSON object. And, here in this blog entry, I'm going to provide you with another class that provides JSON support. So, hopefully someday we can get this class and the others listed on the json.org site (anyone know who to call or write to?).

Why Another JSON Class?
So, if Rick and FoxWeb already provide classes, it begs the question, "Why did I decide to write one too?". Well...

  • When I started working on this I didn't know that Rick even provided such a class in web connect. As for FoxWeb, I've never used it, so I don't know what their object does. Heck, for all I know there are other classes out there besides these two. If you know of any others, please feel free to comment on this blog entry with a link.
  • My JSON class provides additional support for some things when compared to the functionality of wwJSONSerializer:
    • Multi-dimensional array support
    • Replacer support on stringify
    • Reviver support on parse
    • Beautification/indetation support
    • Performance enhancements via FLL
    • A few other things, but those are the main ones
  • The implementation of my JSON class is patterned after json2.js (by Douglas Crockford), which means:
    • The main methods are Stringify() and Parse()
    • It supports datetimes in ISO8601 format (other formats available)
    • Provides the same default escaping of characters (other modes available).
  • Because I wanted to try my hand at this?

There's no better way that I know of to learn something than to write a wrapper or even a class that directly interacts with another library or API. So, I wrote a top-notch JSON class library. I am using it in a production project at this end (my customer was kind enough to give me permission to share it) and I am blogging about it in the hopes some of you may find it useful.

How to Use the JSON Class
In the class, I have provided lots of comments in the code to help document this thing. Also, provided in the download is a jsontests.prg that provides some sample use code you can look at to get started. For the sake of blog brevity... in order to serialize a Visual FoxPro object to JSON you'd do the following:

m.loJSON = CREATEOBJECT("JSON")
SET CLASSLIB TO (LOCFILE("JSON.VCX", "VCX", "Locate JSON Class Library")) additive
m.loObjectIn = CREATEOBJECT("Custom"
m.lcObjectJSON = m.loJSON.Stringify(m.loObjectIn, null, CHR(9))
?m.lcObjectJSON

...and then to deserialize that JSON string you'd do this:

m.loObjectOut = m.loJSON.Parse(m.lcObjectJSON)
?m.loObjectOut.Class

As I say, the code is heavily commented (so check it out) and I'll be doing a blog entry in the near future regarding a Virtual Earth class library I've written for VFP that shows some real world uses for JSON within VFP.

Special Thank You
I sincerely appreciate the efforts of those of you that have already been providing valuable feedback, such as Steven Black and Marc Lyon. Also, Rick Strahl, who provided help and sage advice to me via Twitter as I worked through the particulars of this class. It's always nice to have someone advising you who has already "been there, done that".

What's Next
I'm hopeful that those of you that download this class will take the time to provide me feedback and any suggestions you may have for further improving it.

Until next time... Visual FoxPro Rocks!

Download the JSON class library (approx. 52 KB)

The download contains the JSON class in both VCX and PRG format. Use whichever one you are more comfortable with (they are the same).

Monday, November 24, 2008 12:38:28 PM (GMT Standard Time, UTC+00:00)  #    Comments [9]
Wednesday, November 26, 2008 2:14:13 PM (GMT Standard Time, UTC+00:00)
Hey Craig,

I have next to no idea what this is about, but, knowing your passion for VFP, I'm willing to bet that it's gonna be important! I'm looking forward to your entry about the real world use for JSON in VFP. I think that's when it might all make sense :)
MikeS
Sunday, December 07, 2008 12:09:35 PM (GMT Standard Time, UTC+00:00)
Craig,

how wonderful. I just fell over the article and will have a look into it. We're doing *all* our webstuff using vfp and I'm currently on a project using YUI framework and AJAX on the client side.

I have written a bunch of JSON-Methods myself, absolutely suitable for my current needs but I'm sure this will give me better support.

Frank
Sunday, December 07, 2008 1:13:24 PM (GMT Standard Time, UTC+00:00)
Craig,

Just a quick response. Trying the JSONTests.prg, the numeric and most of the date related tests failed for me, living here in Germany. Also Cursor-Test 4 failed

Over here in Germany we use "." to separate thousands and "," as a decimal separator, thus 1,234 turns into 1. This was easy to fix:

PROTECTED PROCEDURE serializenumber
LPARAMETERS tnNumber

*....

LOCAL lcReturnJSONNumber, lcWasPoint, lcWasSepara

*-- DD changed on 07.12.08
*-- respect local settings
m.lcWasPoint = set("Point")
m.lcWasSepara= set("Separator")
set Point To "."
set Separator to ","

m.lcReturnJSONNumber = TRANSFORM(m.tnNumber)

set Point to m.lcWasPoint
set Separator to m.lcWasSepara

RETURN (m.lcReturnJSONNumber)
ENDPROC

Probably better directly put this into stringify. This way it only would have to be set once.

Investigating the Date-test, I see that it produces this JSON for the current date: "2008-12-07T00:00:00+-1"
which deserializes to m.ldDateOut=06.12.08 23:00:00

respectively the dateTime-test produce an hour time difference. I assume it has something to do with UTC-time-correction. I'll have a closer look into that matter.

Did not look at the cursor Test 4 yet. It first complained about Field 4, now after my correction on "SerializeNumber" it does not like Field 5 .... I see, first it was a numeric value now it falls over the dateTime. So I guess the cursor will pass as soon as I fix the time-diff-problem.

that's it for now from Berlin

Regards

Frank

Sunday, December 07, 2008 2:35:45 PM (GMT Standard Time, UTC+00:00)
.... OK, as it seems the usage oft the TimeZoneOffset is not correct. We currently are one hour ahead of UTC and GetTimeZoneInformation() returns -60 which according to the MS doc is correct as they say "UTC=local time + bias" (http://msdn.microsoft.com/en-us/library/ms724421(VS.85).aspx)

JSON.prg calculates Offset *-60 to get the offset in seconds which goes in the wrong direction, which when serializing and deserializing with the same control does not make a difference. However the transformation to string creates the "+-1" that causes IN and OUT values not to be the same.

I also modified the GetTimezoneOffset() method so that the calculation is only done once and kept as a local property. This way the DLL only needs to be loaded once. I also do clear it after usage.

Some time ago I struggled with this time conversion for my mailing system and created a class that is able to convert detect and convert all the datetime-formats used in mailheaders. This class does not use the dll-call (plain VFP there) but respects daylight saving borders.

That's one thing the JSON-Class does not take care off. If I work with a date/dateime from the summer, it will give me an incorrect UTC-time as we have an offset of 2hrs during summer and 1hr during winter. GetTimeZoneInformation() however returns the current offset.


I'll keep on playing :-D

Frank
Sunday, December 07, 2008 4:20:09 PM (GMT Standard Time, UTC+00:00)
.... OK, found it. In getISO8601TimezoneOffset() a possibly negative prefix needs to be stripped after the +/-/z evaluation prior the transform() operation. Now allt tests pass

PROTECTED PROCEDURE getiso8601timezoneoffset
LOCAL lcReturnTZOffset, lnMinOffset, lcOperator

*********************************************************************
*!* PURPOSE
*********************************************************************
*!* Return ISO8601 suffix for the UTC timezone or Local timezone
*!* offest from GMT based on the setting of This.UseUTCDatetime
*********************************************************************
*!* RETURN
*********************************************************************
*!* String - representing the HH:MM offset from GMT
*********************************************************************

LOCAL lcHours, lcMinutes, lcOperator, lcReturnTZOffset, lnMinOffset

m.lcReturnTZOffset = ""

IF THIS.UseUTCDatetime && GTM Timezone
m.lcReturnTZOffset = "Z"
ELSE
m.lnMinOffset = THIS.GetTimeZoneOffset()
DO CASE
CASE ISNULL(m.lnMinOffset)
*!* Do Nothing
CASE m.lnMinOffset > 0
m.lcOperator = "-"
CASE m.lnMinOffset < 0
m.lcOperator = "+"
OTHERWISE
m.lcReturnTZOffset = "Z"
ENDCASE

IF EMPTY(m.lcReturnTZOffset)

*-- DD changed on 07.12.08
*-- Offset comes in minutes either positive or negative.
*-- As we already have set the prefix we need to make it
*-- neutral here. Otherwise we end up with a +-HH:MM as soon
*-- as the offset is negative
m.lnMinOffset = abs(m.lnMinOffset)

m.lcMinutes = PADL(TRANSFORM(MOD(m.lnMinOffset,60)),2,"0")
m.lcHours = PADL(TRANSFORM(INT(m.lnMinOffset/60)),2,"0")
m.lcReturnTZOffset = m.lcOperator + m.lcHours + ":" + m.lcMinutes
ENDIF
ENDIF

RETURN (m.lcReturnTZOffset)
ENDPROC

I'll send a copy of what I've done to Your code per mail.

regards

Frank
Monday, December 08, 2008 2:03:25 AM (GMT Standard Time, UTC+00:00)
Frank,

Awesome job! I wish all feedback I received was this thorough. Got your email and I'll encorporate the changes you've made into the JSON library tonight. Thank you.
Monday, September 07, 2009 9:34:27 PM (GMT Daylight Time, UTC+01:00)
Hi,
Accessing JSON service from foxpro is something I'm interesting in trying. How do you do the actual GET's, PUT's, etc... to the REST service? I looked at your vfpconnection.fll but don't see away to specify JSON via HTTPToStr(sURI).
Thanks,
Dana
Dana
Wednesday, November 04, 2009 7:45:16 PM (GMT Standard Time, UTC+00:00)
Good morning. People fail forward to success. Help me! Please help find sites for: Banks that are refinancing. I found only this - central bank refinancing. Milam has quoted the licensed environmental health practitioner assistance and is offering her payment securities.However, in the deferred loan our awareness is to need with payable short lenders to remain rationalize a round of structure and cause the services corrections by extending a access in the inventory. Nightmare i shows an application of the role of the number management of a aggregate peer. THX :confused:, Kemal from Uganda.
Monday, April 19, 2010 9:20:13 PM (GMT Daylight Time, UTC+01:00)
Craig, Great work!
I did encounter a bug, though, in Parse:
If lvValue gets DIMENSION'ed as an array, then simply setting it back to .NULL. isn't enough to reset the variable... it just puts .NULL. in each of the array cells (and every assignment from then on is filling the array, rather than just assigning one value, which might have an impact on performance.)

This isn't noticeable except if a subsequent call to Parse generates an Object, at which point VFP throws the "Cannot assign an object to an array" error.
I was able to fix the problem by adding these two lines just before "m.lvValue = NULL" in the method "deserializeobject":
RELEASE lvValue
LOCAL lvValue
I ran into this when using JSON.PRG to decode the response from a call to the Google Maps Geocoder

Thanks for a great tool!
William Steinford
All comments require the approval of the site owner before being displayed.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, strike, strong, sub, sup, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview

 

Archive

<October 2014>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678