├── Custom Functions ├── DateFromISO.fmfn ├── ISOFromDate.fmfn ├── ISOFromTime.fmfn ├── ISOFromTimestamp.fmfn ├── JSONContainerObject.fmfn ├── JSONGetBoolean.fmfn ├── JSONGetContainer.fmfn ├── JSONGetDateFromISO.fmfn ├── JSONGetNumber.fmfn ├── JSONGetTimeFromISO.fmfn ├── JSONGetTimestampFromISO.fmfn ├── TimeFromISO.fmfn └── TimestampFromISO.fmfn ├── Data Types and JSON.fmp12 └── README.md /Custom Functions/DateFromISO.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * DateFromISO ( iso8601Date ) 3 | * Parses date data formatted according to ISO 8601, and casts it to a 4 | * FileMaker date. Dates with limited accuracy will return the first date in the 5 | * specified period. 6 | * 7 | * @parameter iso8601Date: text encoding a date in ISO 8601 format 8 | * 9 | * @return A date 10 | * 11 | * @history 2013-01-25 - Jeremy Bante - Created 12 | * @history 2017-04-06 - Jeremy Bante - Expanding to 13 | * additional allowed formats 14 | * @history 2017-04-08 - Jeremy Bante - Refactoring 15 | * @history 2017-04-25 - Daniel Smith - Add 16 | * exceptions for JSONGetElement error and empty parameter 17 | * @history 2017-04-27 - Jeremy Bante - Removing exception 18 | * for JSONGetElement result error. Adjusting response to empty date input to 19 | * behave similarly for timestamp input. 20 | * @history 2017-10-02 - Jeremy Bante - Tolerating spaces 21 | * as timestamp delimiters. 22 | * 23 | * @see https://en.wikipedia.org/wiki/ISO_8601 24 | ******************************************************************************/ 25 | 26 | Let ( [ 27 | _delimiterPosition = 28 | Max ( 29 | Position ( iso8601Date ; "T" ; 1 ; 1 ) ; 30 | Position ( iso8601Date ; " " ; 1 ; 1 ) 31 | ) ; 32 | _text = 33 | If ( _delimiterPosition = 0 ; 34 | iso8601Date ; 35 | /* Else */ Left ( iso8601Date ; _delimiterPosition - 1 ) 36 | ) ; 37 | 38 | _firstHyphen = Position ( _text ; "-" ; 1 ; 1 ) ; 39 | _totalLength = Length ( _text ) ; 40 | _invalid = _firstHyphen = 1 or _totalLength < 7 ; 41 | _hyphenFormat = _firstHyphen > 0 ; 42 | _weekFormat = Middle ( _text ; If ( _hyphenFormat ; 6 ; 5 ) ; 1 ) = "W" ; 43 | _ordinalFormat = 44 | not _weekFormat and _totalLength = If ( _hyphenFormat ; 8 ; 7 ) ; 45 | 46 | _year = Left ( _text ; 4 ) 47 | ] ; 48 | Case ( 49 | IsEmpty ( _text ) ; "" ; 50 | 51 | _invalid ; "? Not a valid ISO 8601 date¶ " & iso8601Date ; 52 | 53 | _weekFormat ; 54 | Let ( [ 55 | _jan4 = Date ( 1 ; 4 ; _year ) ; 56 | _jan4DayOfISOWeek = Mod ( DayOfWeek ( _jan4 ) - 2 ; 7 ) ; 57 | _startOfFirstISOWeek = _jan4 - _jan4DayOfISOWeek ; 58 | _week = 59 | Middle ( _text ; If ( _hyphenFormat ; 7 ; 6 ) ; 2 ) ; 60 | _day = 61 | Middle ( _text ; If ( _hyphenFormat ; 10 ; 8 ) ; 3 ) ; 62 | _day = If ( IsEmpty ( _day ) ; 1 ; /* Else */ _day ) 63 | ] ; 64 | _startOfFirstISOWeek + 7 * ( _week - 1 ) + _day - 1 65 | ) ; 66 | 67 | _ordinalFormat ; 68 | Date ( 69 | 1 ; 70 | Middle ( _text ; If ( _hyphenFormat ; 6 ; 5 ) ; 3 ) ; 71 | _year 72 | ) ; 73 | 74 | /* Else */ 75 | Let ( [ 76 | _month = Middle ( _text ; If ( _hyphenFormat ; 6 ; 5 ) ; 2 ) ; 77 | _day = Middle ( _text ; If ( _hyphenFormat ; 9 ; 7 ) ; 2 ) ; 78 | _day = If ( IsEmpty ( _day ) ; 1 ; /* Else */ _day ) 79 | ] ; 80 | Date ( _month ; _day ; _year ) 81 | ) 82 | ) 83 | ) -------------------------------------------------------------------------------- /Custom Functions/ISOFromDate.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * ISOFromDate ( theDate ) 3 | * 4 | * @parameter theDate: The FileMaker date to coerce to ISO 8601 format 5 | * 6 | * @return theDate rendered in ISO 8601 format: YYYY-MM-DD 7 | * 8 | * @history 2011-09-22 - Jeremy Bante - Created 9 | * @history 2017-05-11 - Jeremy Bante - Adding error 10 | * result for non-date input 11 | * 12 | * @see http://en.wikipedia.org/wiki/ISO_8601 13 | ******************************************************************************/ 14 | 15 | Let ( [ 16 | _date = 17 | If ( Year ( theDate ) = "?" ; 18 | GetAsTimestamp ( theDate ) ; 19 | /* Else */ theDate 20 | ) ; 21 | _year = Year ( _date ) 22 | ] ; 23 | Case ( 24 | IsEmpty ( theDate ) ; 25 | "" ; 26 | 27 | _year = "?" ; 28 | "? Value is not a date¶ " & theDate ; 29 | 30 | /* Else */ 31 | Right ( "0000" & Year ( _date ) ; 4 ) 32 | & "-" 33 | & Right ( "00" & Month ( _date ) ; 2 ) 34 | & "-" 35 | & Right ( "00" & Day ( _date ) ; 2 ) 36 | ) 37 | ) -------------------------------------------------------------------------------- /Custom Functions/ISOFromTime.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * ISOFromTime ( theTime ) 3 | * 4 | * @parameter theTime: The FileMaker time to convert to ISO 8601 format 5 | * 6 | * @return theTime rendered in ISO 8601 format: hh:mm:ss,sss (24-hour time) 7 | * 8 | * @history 2017-04-16 - Jeremy Bante - Created 9 | * @history 2017-05-11 - Jeremy Bante - Improving response 10 | * to error conditions. 11 | * 12 | * @see http://en.wikipedia.org/wiki/ISO_8601 13 | ******************************************************************************/ 14 | 15 | Let ( [ 16 | _time = 17 | If ( theTime ≥ Time ( 24 ; 0 ; 0 ) ; 18 | GetAsTimestamp ( theTime ) ; 19 | /* Else */ theTime 20 | ) ; 21 | _seconds = Seconds ( _time ) ; 22 | _integerSeconds = Div ( _seconds ; 1 ) ; 23 | _fractionSeconds = Mod ( _seconds ; 1 ) 24 | ] ; 25 | Case ( 26 | IsEmpty ( theTime ) ; 27 | "" ; 28 | 29 | _seconds = "?" ; 30 | "? Value is not a time¶ " & theTime ; 31 | 32 | /* Else */ 33 | Right ( "00" & Hour ( _time ) ; 2 ) 34 | & ":" & Right ( "00" & Minute ( _time ) ; 2 ) 35 | & If ( _seconds > 0 ; 36 | ":" & Right ( "00" & _integerSeconds ; 2 ) 37 | ) 38 | & If ( _fractionSeconds > 0 ; 39 | "," & Replace ( _fractionSeconds ; 1 ; 1 ; "" ) 40 | ) 41 | ) 42 | ) -------------------------------------------------------------------------------- /Custom Functions/ISOFromTimestamp.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * ISOFromTimestamp ( theTimestamp ) 3 | * 4 | * @parameter theTimestamp: The FileMaker timestamp to coerce to ISO 8601 format 5 | * 6 | * @return theTimestamp rendered in ISO 8601 format: YYYY-MM-DDThh:mm:ss,sss 7 | * (24-hour time) 8 | * 9 | * @uses ISOFromDate ( theDate ) 10 | * @uses ISOFromTime ( theTime ) 11 | * 12 | * @history 2011-05-17 - Jeremy Bante - Created 13 | * @history 2017-04-16 - Jeremy Bante - Refactoring to use 14 | * other functions for date & time components. 15 | * @history 2017-05-11 - Jeremy Bante - Improving error 16 | * results. 17 | * 18 | * @see http://en.wikipedia.org/wiki/ISO_8601 19 | ******************************************************************************/ 20 | 21 | Let ( [ 22 | _date = ISOFromDate ( theTimestamp ) ; 23 | _time = ISOFromTime ( theTimestamp ) ; 24 | _notValid = Left ( _date ; 1 ) = "?" or Left ( _time ; 1 ) = "?" 25 | ] ; 26 | Case ( 27 | IsEmpty ( theTimestamp ) ; 28 | "" ; 29 | 30 | _notValid ; 31 | "? Value is not a timestamp¶ " & theTimestamp ; 32 | 33 | /* Else */ 34 | _date & "T" & _time 35 | ) 36 | ) -------------------------------------------------------------------------------- /Custom Functions/JSONContainerObject.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * JSONContainerObject ( containerData ) 3 | * Encodes container data as a JSONObject. 4 | * 5 | * @parameter containerData 6 | * 7 | * @return a JSONObject encoding the container data 8 | * 9 | * @history 2017-04-16 - Jeremy Bante - Created 10 | * @history 2017-04-27 - Jeremy Bante - Using 11 | * Base64EncodeRFC to improve speed, implementing a recommendation by Dan Smith. 12 | * @history 2017-05-28 - Jeremy Bante - Handling empty 13 | * inputs based on a suggestion by Dale Long. 14 | * @see http://www.modularfilemaker.org/module/fm-json-types/#comment-30235 15 | ******************************************************************************/ 16 | 17 | If ( IsEmpty ( containerData ) ; 18 | "{}" ; 19 | /* Else */ 20 | JSONSetElement ( "{}" ; 21 | [ "fileName" ; GetContainerAttribute ( containerData ; "filename" ) ; JSONString ] ; 22 | [ "base64" ; Base64EncodeRFC ( 4648 ; containerData ) ; JSONString ] 23 | ) 24 | ) -------------------------------------------------------------------------------- /Custom Functions/JSONGetBoolean.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * JSONGetBoolean ( json ; keyOrIndexOrPath ) 3 | * Parses a value from JSON data, and casts it to a boolean. 4 | * 5 | * @parameter json: the json to parse 6 | * @parameter keyOrIndexOrPath: which value to parse out 7 | * 8 | * @return A boolean, or text indicating an error 9 | * 10 | * @history 2017-05-11 - Jeremy Bante - Created 11 | ******************************************************************************/ 12 | 13 | Let ( [ 14 | _value = JSONGetElement ( json ; keyOrIndexOrPath ) ; 15 | _thereWasAParsingError = ( Left ( _value ; 1 ) = "?" ) ; 16 | _valueIsNotABoolean = not ( _value = False or _value = True ) 17 | ] ; 18 | Case ( 19 | _thereWasAParsingError ; _value ; 20 | _valueIsNotABoolean ; "? Value is not a boolean¶ " & _value ; 21 | /* Else */ _value 22 | ) 23 | ) -------------------------------------------------------------------------------- /Custom Functions/JSONGetContainer.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * JSONGetContainer ( json ; keyOrIndexOrPath ) 3 | * Parses a value from JSON data, and casts it as FileMaker container data. 4 | * This function assumes that the keyOrIndexOrPath refers to a JSONObject 5 | * encoded according to the format of the JSONContainerObject function. 6 | * 7 | * @parameter json: the json to parse 8 | * @parameter keyOrIndexOrPath: which value to parse out 9 | * 10 | * @return container data 11 | * 12 | * @history 2017-04-16 - Jeremy Bante - Created 13 | ******************************************************************************/ 14 | 15 | Let ( [ 16 | _value = JSONGetElement ( json ; keyOrIndexOrPath ) ; 17 | _thereWasAParsingError = Left ( _value ; 1 ) = "?" 18 | ] ; 19 | If ( _thereWasAParsingError ; 20 | _value ; 21 | /* Else */ 22 | Base64Decode ( 23 | JSONGetElement ( _value ; "base64" ) ; 24 | JSONGetElement ( _value ; "fileName" ) 25 | ) 26 | ) 27 | ) -------------------------------------------------------------------------------- /Custom Functions/JSONGetDateFromISO.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * JSONGetDateFromISO ( json ; keyOrIndexOrPath ) 3 | * Parses a value from JSON data, and casts it to a FileMaker date. 4 | * This function assumes that the date is encoded as a JSONString formatted 5 | * according to ISO 8601. Dates with limited accuracy will return the first date 6 | * in the specified period. 7 | * 8 | * @parameter json: the json to parse 9 | * @parameter keyOrIndexOrPath: which value to parse out 10 | * 11 | * @return A date 12 | * 13 | * @history 2017-04-06 - Jeremy Bante - Created 14 | * @history 2017-04-08 - Jeremy Bante - Refactoring 15 | * 16 | * @see https://en.wikipedia.org/wiki/ISO_8601 17 | ******************************************************************************/ 18 | 19 | Let ( [ 20 | _value = JSONGetElement ( json ; keyOrIndexOrPath ) ; 21 | _thereWasAParsingError = Left ( _value ; 1 ) = "?" 22 | ] ; 23 | If ( _thereWasAParsingError ; _value ; /* Else */ DateFromISO ( _value ) ) 24 | ) -------------------------------------------------------------------------------- /Custom Functions/JSONGetNumber.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * JSONGetNumber ( json ; keyOrIndexOrPath ) 3 | * Parses a value from JSON data, and casts it to a number. 4 | * 5 | * @parameter json: the json to parse 6 | * @parameter keyOrIndexOrPath: which value to parse out 7 | * 8 | * @return A number 9 | * 10 | * @history 2017-04-06 - Jeremy Bante - Created 11 | * @history 2017-04-08 - Jeremy Bante - Refactoring 12 | * @history 2017-04-25 - Daniel Smith - Add value 13 | * to result if it's not a number. 14 | * @history 2017-04-27 - Jeremy Bante - Omitting input 15 | * from error result. 16 | * @history 2017-04-30 - Jeremy Bante - Revising wording 17 | * of NaN error result and re-including parsed value, based on discussion with 18 | * Dan Smith. 19 | ******************************************************************************/ 20 | 21 | Let ( [ 22 | _value = JSONGetElement ( json ; keyOrIndexOrPath ) ; 23 | _thereWasAParsingError = ( Left ( _value ; 1 ) = "?" ) ; 24 | _number = GetAsNumber ( _value ) ; 25 | _valueIsNotANumber = IsEmpty ( _number ) and not IsEmpty ( _value ) 26 | ] ; 27 | Case ( 28 | _thereWasAParsingError ; _value ; 29 | _valueIsNotANumber ; "? Value is not a number¶ " & _value ; 30 | /* Else */ _number 31 | ) 32 | ) -------------------------------------------------------------------------------- /Custom Functions/JSONGetTimeFromISO.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * JSONGetTimeFromISO ( json ; keyOrIndexOrPath ) 3 | * Parses a value from JSON data, and casts it to a FileMaker time. 4 | * This function assumes that the time is encoded as a JSONString formatted 5 | * according to ISO 8601. Times with limited accuracy will return the first time 6 | * in the specified period. 7 | * 8 | * @parameter json: the json to parse 9 | * @parameter keyOrIndexOrPath: which value to parse out 10 | * 11 | * @return A time 12 | * 13 | * @history 2017-04-16 - Jeremy Bante - Created 14 | * 15 | * @see https://en.wikipedia.org/wiki/ISO_8601 16 | ******************************************************************************/ 17 | 18 | Let ( [ 19 | _value = JSONGetElement ( json ; keyOrIndexOrPath ) ; 20 | _thereWasAParsingError = Left ( _value ; 1 ) = "?" 21 | ] ; 22 | If ( _thereWasAParsingError ; _value ; /* Else */ TimeFromISO ( _value ) ) 23 | ) -------------------------------------------------------------------------------- /Custom Functions/JSONGetTimestampFromISO.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * JSONGetTimestampFromISO ( json ; keyOrIndexOrPath ) 3 | * Parses a value from JSON data, and casts it to a FileMaker timestamp. 4 | * This function assumes that the timestamp is encoded as a JSONString formatted 5 | * according to ISO 8601. Timestamps with limited accuracy will return the first 6 | * timestamp in the specified period. 7 | * 8 | * @parameter json: the json to parse 9 | * @parameter keyOrIndexOrPath: which value to parse out 10 | * 11 | * @return A timestamp 12 | * 13 | * @history 2017-04-16 - Jeremy Bante - Created 14 | * 15 | * @see https://en.wikipedia.org/wiki/ISO_8601 16 | ******************************************************************************/ 17 | 18 | Let ( [ 19 | _value = JSONGetElement ( json ; keyOrIndexOrPath ) ; 20 | _thereWasAParsingError = Left ( _value ; 1 ) = "?" 21 | ] ; 22 | If ( _thereWasAParsingError ; 23 | _value ; 24 | /* Else */ TimestampFromISO ( _value ) 25 | ) 26 | ) -------------------------------------------------------------------------------- /Custom Functions/TimeFromISO.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * TimeFromISO ( iso8601Time ) 3 | * Parses time data formatted according to ISO 8601, and casts it to a 4 | * FileMaker time. For time with limited precision, the first time in the 5 | * specified period will be returned. 6 | * 7 | * @parameter iso8601Time: text encoding a time in ISO 8601 format 8 | * 9 | * @return A time 10 | * 11 | * @history 2017-04-16 - Jeremy Bante - Created 12 | * @history 2017-04-25 - Daniel Smith - Add 13 | * exceptions for JSONGetElement error and empty parameter 14 | * @history 2017-04-27 - Jeremy Bante - Removing exception 15 | * for JSONGetElement result error. Adjusting response to empty date input to 16 | * behave similarly for timestamp input. 17 | * @history 2017-05-11 - Jeremy Bante - Improving error 18 | * result 19 | * @history 2017-10-02 - Jeremy Bante - Tolerating space 20 | * as timestamp delimiter. 21 | * 22 | * @see https://en.wikipedia.org/wiki/ISO_8601 23 | ******************************************************************************/ 24 | 25 | Let ( [ 26 | _inputIsEmpty = IsEmpty ( iso8601Time ) ; 27 | _delimiterPosition = 28 | Max ( 29 | Position ( iso8601Time ; "T" ; 1 ; 1 ) ; 30 | Position ( iso8601Time ; " " ; 1 ; 1 ) 31 | ) ; 32 | _text = 33 | If ( _delimiterPosition = 0 ; 34 | iso8601Time ; 35 | /* Else */ Replace ( iso8601Time ; 1 ; _delimiterPosition ; "" ) 36 | ) ; 37 | _timezonePosition = 38 | Max ( 39 | Position ( _text ; "Z" ; 1 ; 1 ) ; 40 | Position ( _text ; "+" ; 1 ; 1 ) ; 41 | Position ( _text ; "-" ; 1 ; 1 ) ; 42 | Position ( _text ; "−" ; 1 ; 1 ) 43 | ) ; 44 | _invalid = 45 | ( IsEmpty ( _text ) and not _inputIsEmpty ) 46 | or ( 47 | _timezonePosition > 0 48 | and PatternCount ( _text ; Middle ( _text ; _timezonePosition ; 1 ) ) > 1 49 | ) ; 50 | _text = 51 | If ( _timezonePosition = 0 ; 52 | _text ; 53 | /* Else */ Left ( _text ; _timezonePosition - 1 ) 54 | ) ; 55 | _colonFormat = Position ( _text ; ":" ; 1 ; 1 ) > 0 ; 56 | _decimal = 57 | Max ( Position ( _text ; "," ; 1 ; 1 ) ; Position ( _text ; "." ; 1 ; 1 ) ) ; 58 | _integer = 59 | If ( _decimal = 0 ; 60 | _text ; 61 | /* Else */ Left ( _text ; _decimal - 1 ) 62 | ) ; 63 | 64 | _hour = Left ( _integer ; 2 ) ; 65 | _minute = Middle ( _integer ; If ( _colonFormat ; 4 ; 3 ) ; 2 ) ; 66 | _second = Middle ( _integer ; If ( _colonFormat ; 7 ; 5 ) ; 2 ) 67 | ] ; 68 | Case ( 69 | _inputIsEmpty ; 70 | "" ; 71 | 72 | _invalid ; 73 | "? Not a valid ISO 8601 time¶ " & iso8601Time ; 74 | 75 | _decimal ≠ 0 ; 76 | Let ( [ 77 | _fraction = Replace ( _text ; 1 ; _decimal ; "" ) ; 78 | _fraction = _fraction * 10 ^ ( 0 - Length ( _fraction ) ) 79 | ] ; 80 | Case ( 81 | IsEmpty ( _minute ) ; 82 | Time ( _hour + _fraction ; 0 ; 0 ) ; 83 | IsEmpty ( _second ) ; 84 | Time ( _hour ; _minute + _fraction ; 0 ) ; 85 | /* Else */ 86 | Time ( _hour ; _minute ; _second + _fraction ) 87 | ) 88 | ) ; 89 | 90 | /* Else */ 91 | Time ( _hour ; _minute ; _second ) 92 | ) 93 | 94 | ) -------------------------------------------------------------------------------- /Custom Functions/TimestampFromISO.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * TimestampFromISO ( iso8601Timestamp ) 3 | * Parses timestamp data formatted according to ISO 8601, and casts it to a 4 | * FileMaker timestamp. For timestamps with limited precision, the first time in 5 | * the specified period will be returned. 6 | * 7 | * @parameter iso8601Timestamp: text encoding a time in ISO 8601 format 8 | * 9 | * @return A timestamp 10 | * 11 | * @uses DateFromISO ( iso8601Date ) 12 | * @uses TimeFromISO ( iso8601Time ) 13 | * 14 | * @history 2017-04-16 - Jeremy Bante - Created 15 | * @history 2017-05-11 - Jeremy Bante - Improving error 16 | * result. 17 | * @history 2017-10-02 - Jeremy Bante - Tolerating spaces 18 | * as timestamp delimiters. 19 | * 20 | * @see https://en.wikipedia.org/wiki/ISO_8601 21 | ******************************************************************************/ 22 | 23 | Let ( [ 24 | _date = DateFromISO ( iso8601Timestamp ) ; 25 | _delimiterIsPresent = 26 | Position ( iso8601Timestamp ; "T" ; 1 ; 1 ) > 0 27 | or Position ( iso8601Timestamp ; " " ; 1 ; 1 ) > 0 ; 28 | _time = 29 | If ( _delimiterIsPresent ; 30 | TimeFromISO ( iso8601Timestamp ) ; 31 | /* Else */ 0 32 | ) ; 33 | _error = 34 | Left ( _date ; 1 ) = "?" 35 | or Left ( _time ; 1 ) = "?" 36 | or _delimiterIsPresent and ( IsEmpty ( _date ) or IsEmpty ( _time ) ) 37 | ] ; 38 | If ( _error ; 39 | "? Not a valid ISO 8601 timestamp¶ " & iso8601Timestamp ; 40 | /* Else */ 41 | Timestamp ( _date ; _time ) 42 | ) 43 | ) -------------------------------------------------------------------------------- /Data Types and JSON.fmp12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbante/FM-JSON-Types/1359b9124d17a735e79d79cafc4ca8a65f70d148/Data Types and JSON.fmp12 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FileMaker JSON Type Handling 2 | 3 | FileMaker 16 introduced [a collection of built-in functions][1] for manipulating data serialized as JSON. This makes it easier for FileMaker applications to interact with many web services. This will also make JSON the de facto standard format for scripts within FileMaker to pass parameters and results to each other, improving code sharing within the FileMaker community. 4 | 5 | [1]: https://fmhelp.filemaker.com/help/16/fmp/en/#page/FMP_Help/json-functions.html "FileMaker documentation on JSON functions" 6 | 7 | JSON does not have a broad palette of scalar data types to choose from: text, number, boolean, and null. On top of that, incoming JSON data may inappropriately format boolean or numeric data as text. Sending and receiving data with correct types requires more logic on top of the new JSON functions. 8 | 9 | This module uses [ISO 8601 format][2] for dates, times, and timestamps. This is the most popular method for representing such data used by web services, so this module is also useful for integrating web services with FileMaker applications. 10 | 11 | [2]: https://en.wikipedia.org/wiki/ISO_8601 "ISO 8601 on Wikipedia" 12 | 13 | This module supports two approaches for translating between FileMaker data types and JSON data: 14 | 1. Custom functions - This approach is convenient to read and write. However, copying logic using the custom functions from one file to another is more complicated, since the custom functions must also be copied, and they must be copied _before_ any dependent logic. 15 | 2. Scripts - This approach is convenient to copy and paste between FileMaker files as part of a module script folder. However, code using these scripts for data translation is more cumbersome than using the custom functions. 16 | 17 | ## Installation 18 | 19 | 1. Import all the custom functions from the FileMaker file into your application. You can skip this step if you only want to use scripts to translate typed data between FileMaker and JSON. 20 | 2. Import the "Modules" / "FM-JSON Types" script folder and all its contents from the FileMaker file into your application. 21 | 22 | ## How to use the custom functions 23 | 24 | ###### Set a number: 25 | 26 | JSONSetElement ( "{}" ; "numberKey" ; 1.23e+4 ; JSONNumber ) 27 | = {"numberKey":1.23e+4} 28 | 29 | ###### Get a number: 30 | 31 | JSONGetNumber ( $json ; "numberKey" ) 32 | = 1.23e+4 33 | 34 | Since JSON supports number-type data, no function is necessary when serializing to JSON. The JSONGetElement function in FileMaker 16.0.2 and later will automatically return number-type data if the element is a JSON number. However, the JSONGetNumber custom function may still be useful for handling data from sources that inappropriately format numeric data as JSON strings. 35 | 36 | ###### Set a boolean: 37 | 38 | JSONSetElement ( "{}" ; "booleanKey" ; False ; JSONBoolean ) 39 | = {"booleanKey":false} 40 | 41 | ###### Get a boolean: 42 | 43 | JSONGetBoolean ( $json ; "booleanKey" ) 44 | = 0 45 | 46 | Since JSON supports boolean-type data, no function is necessary when serializing to JSON. The JSONGetElement function in FileMaker 16.0.2 and later will automatically return boolean-type data if the element is a JSON boolean. (Note that FileMaker represents boolean values as numbers, 0 for _false_ and 1 for _true_.) However, the JSONGetBoolean custom function may still be useful for handling data from sources that inappropriately format boolean data as JSON strings. 47 | 48 | ###### Set a date: 49 | 50 | JSONSetElement ( "{}" ; "dateKey" ; ISOFromDate ( Date ( 5 ; 9 ; 2017 ) ) ; JSONString ) 51 | = {"dateKey":"2017-05-09"} 52 | 53 | ###### Get a date: 54 | 55 | JSONGetDateFromISO ( $json ; "dateKey" ) 56 | = 9 May 2017 57 | 58 | Dates are formatted according to ISO 8601 and serialized as JSONStrings. 59 | 60 | ###### Set a time: 61 | 62 | JSONSetElement ( "{}" ; "timeKey" ; ISOFromTime ( Time ( 18 ; 34 ; 56.7 ) ) ; JSONString ) 63 | = {"timeKey":"18:34:56,7"} 64 | 65 | ###### Get a time: 66 | 67 | JSONGetTimeFromISO ( $json ; "timeKey" ) 68 | = 6:34:56.7 pm 69 | 70 | Times are formatted according to ISO 8601 and serialized as JSONStrings. ISO 8601 supports time zones, but FileMaker times do not. These functions will ignore time zone information from other sources. 71 | 72 | ###### Set a timestamp: 73 | 74 | JSONSetElement ( "{}" ; "timestampKey" ; ISOFromTimestamp ( $timestamp ) ; JSONString ) 75 | = {"timestampKey":"2017-05-09T12:34:56,7"} 76 | 77 | ###### Get a timestamp: 78 | 79 | JSONGetTimestampFromISO ( $json ; "timestampKey" ) 80 | = 9 May 2017, 6:34:56.7 pm 81 | 82 | Timestamps are formatted according to ISO 8601 and serialized as JSONStrings. ISO 8601 supports time zones, but FileMaker timestamps do not. These functions will ignore time zone information from other sources. 83 | 84 | ###### Set a container: 85 | 86 | JSONSetElement ( "{}" ; "containerKey" ; JSONContainerObject ( $container ) ; JSONObject ) 87 | = {"containerKey":{"base64":"iVBOR...","fileName":"image.png"}} 88 | 89 | ###### Get a container: 90 | 91 | JSONGetContainer ( $json ; "containerKey" ) 92 | 93 | Containers are serialized as JSONObjects with sub-values for the file name and the base 64-encoded binary data. 94 | 95 | ## How to use the scripts 96 | 97 | ###### Set a number: 98 | 99 | Perform Script [ "Your Script" ; Parameter: JSONSetElement ( "{}" ; "numberKey" ; 1.23e+4 ; JSONNumber ) ] 100 | 101 | ###### Get a number: 102 | 103 | Set Variable [ $number ; Value: GetAsNumber ( JSONGetElement ( Get ( ScriptResult ) ; "numberKey" ) ) ] 104 | 105 | Since FileMaker has a built-in `GetAsNumber` function, there is no value in using a separate script to interpret number data from JSON. The JSONGetElement function in FileMaker 16.0.2 and later will automatically return number-type data if the element is a JSON number. However, explicitly casting the value as a number may still be useful for handling data from sources that inappropriately format numeric data as JSON strings. 106 | 107 | ###### Set a boolean: 108 | 109 | Perform Script [ "Your Script" ; Parameter: JSONSetElement ( "{}" ; "booleanKey" ; False ; JSONBoolean ) ] 110 | 111 | ###### Get a boolean: 112 | 113 | Set Variable [ $boolean ; Value: GetAsBoolean ( JSONGetElement ( Get ( ScriptResult ) ; "booleanKey" ) ) ] 114 | 115 | Since FileMaker has a built-in `GetAsBoolean` function, there is no value in using a separate script to interpret boolean data from JSON. The JSONGetElement function in FileMaker 16.0.2 and later will automatically return boolean-type data if the element is a JSON boolean. (Note that FileMaker represents boolean values as numbers, 0 for _false_ and 1 for _true_.) However, explicitly casting the value as a boolean may still be useful for handling data from sources that inappropriately format boolean data as JSON strings. 116 | 117 | ###### Set a date: 118 | 119 | Perform Script [ "Convert to ISO 8601 from Date" ; Parameter: Date ( 5 ; 9 ; 2017 ) ] 120 | Set Variable [ $json ; Value: JSONSetElement ( "{}" ; "dateKey" ; Get ( ScriptResult ) ; JSONString ) ] 121 | 122 | ###### Get a date: 123 | 124 | Perform Script [ "Convert to Date from ISO 8601" ; Parameter: JSONGetElement ( $json ; "dateKey" ) ] 125 | Set Variable [ $date ; Value: Get ( ScriptResult ) ] 126 | 127 | Dates are formatted according to ISO 8601 and serialized as JSONStrings. 128 | 129 | ###### Set a time: 130 | 131 | Perform Script [ "Convert to ISO 8601 from Time" ; Parameter: Time ( 18 ; 34 ; 56.7 ) ] 132 | Set Variable [ $json ; Value: JSONSetElement ( "{}" ; "timeKey" ; Get ( ScriptResult ) ; JSONString ) ] 133 | 134 | ###### Get a time: 135 | 136 | Perform Script [ "Convert to Time from ISO 8601" ; Parameter: JSONGetElement ( $json ; "timeKey" ) ] 137 | Set Variable [ $time ; Value: Get ( ScriptResult ) ] 138 | 139 | Times are formatted according to ISO 8601 and serialized as JSONStrings. ISO 8601 supports time zones, but FileMaker times do not. These scripts will ignore time zone information from other sources. 140 | 141 | ###### Set a timestamp: 142 | 143 | Perform Script [ "Convert to ISO 8601 from Timestamp" ; Parameter: $timestamp ] 144 | Set Variable [ $json ; Value: JSONSetElement ( "{}" ; "timestampKey" ; Get ( ScriptResult ) ; JSONString ) 145 | 146 | ###### Get a timestamp: 147 | 148 | Perform Script [ "Convert to Timestamp from ISO 8601" ; Parameter: JSONGetElement ( $json ; "timestampKey" ) ] 149 | Set Variable [ $timestamp ; Value: Get ( ScriptResult ) ] 150 | 151 | Timestamps are formatted according to ISO 8601 and serialized as JSONStrings. ISO 8601 supports time zones, but FileMaker timestamps do not. These scripts will ignore time zone information from other sources. 152 | 153 | ###### Set a container: 154 | 155 | Perform Script [ "Convert to JSONObject from Container" ; Parameter: $container ] 156 | Set Variable [ $json ; Value: JSONSetElement ( "{}" ; "containerKey" ; Get ( ScriptResult ) ; JSONObject ) ] 157 | 158 | ###### Get a container: 159 | 160 | Perform Script [ "Convert to Container from JSONObject" ; Parameter: JSONGetElement ( $json ; "containerKey" ) ] 161 | Set Variable [ $container ; Value: Get ( ScriptResult ) ] 162 | 163 | Containers are serialized as JSONObjects with sub-values for the file name and the base 64-encoded binary data. 164 | 165 | ## License 166 | 167 | Anyone may do anything with this software for any purpose. There is no warranty. --------------------------------------------------------------------------------