4

I'm trying to define a JSON schema for a property

money: 12.12

My main concern is that a maximum of 2 decimal places should be allowed. My initial attempt at defining this field was

money: {
  type: 'number',
  minimum: 0,
  multipleOf: 0.01
}

However, owing to floating point imprecision this fails. For example, using the tv4 validator, the number 147.41 passes validation, but 147.42 fails. Is there an alternative way to define a numeric type which will only allow a maximum of 2 decimal places?

It seems that the purpose of the "format" attribute is to implement these types of restrictions, but if I define the field like so:

money: {
  type: 'number',
  format: 'currency',
  minimum: 0
}

Then how do I specify that fields with a 'currency' format should only allow up to 2 decimal places?

Dónal
  • 185,044
  • 174
  • 569
  • 824
  • The question being asked here seems more like "How to enforce two decimal places for currency numbers?". The title of this question "Defining a JSON schema currency type" has a better answer here: https://stackoverflow.com/q/30249406/27581 – Michael Kropat Aug 15 '18 at 19:44

2 Answers2

7

I dislike the integer value thing, I find it doesn't solve real issues when it comes to proper number rounding when things like percentages get involved, (which is why other languages have Decimal types) and wanted to offer this very simple solution using string and pattern:

"amount":{
    "type": "string",
    "pattern": "^(0|([1-9]+[0-9]*))(\.[0-9]{1,2})?$",
    "minLength": 1,
    "description": "A Monetary Amount",
    "examples": [
        "0",
        "0.00",
        "0.05",
        "19.95",
        "255.5",
        "120000"
    ]
}

You can remove the minLength if you don't want it.

Francisco
  • 10,918
  • 6
  • 34
  • 45
Tecktron
  • 71
  • 1
  • 2
2

Your best option is likely to multiply your currency values by 100. Then you have whole numbers, and validation should work fine. In general, using floating point to store monetary values is a bug farm, so you're better off moving toward "integers" even if technically they are still floating point values in JSON.

Some systems use an ISO currency code with the third letter in lower case to denote such currencies. For example GBp for British pence, or USd for US pennies. See https://stackoverflow.com/a/30635729/4323

Community
  • 1
  • 1
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Unfortunately, this is not an option for me. The definition of the fields in the schema must correspond to the format entered by the user, and I can't reasonably ask the user to enter a currency amount in cents, pennies, etc. – Dónal Nov 30 '16 at 11:57
  • @Dónal: I understand you would allow users to enter whole dollars. But what stops you from converting for storage? Technically the user enters a string anyway--you are already converting it to a floating point value. – John Zwinck Nov 30 '16 at 12:00
  • There's no easy way for me to intercept the values to convert them from cents to dollars or vice versa. The amounts entered in the form are stored directly (in Firebase). The values defined by the schema will be those that are persisted, and I rely entirely on the schema for validation – Dónal Nov 30 '16 at 12:29