Hi everyone!
I'm having trouble with the validation of JSON. Hope you can help, because it's quite specific and there is not much information online.
This is my current code:
def validate_schema(message, schema_path):
try:
# We encapsulate the code in a try-catch to always
# have a controlled answer
message_json = TypeUtilities.pyToGson(message)
# We load the schema from the file
with open(schema_path) as f:
schema_file = json.load(f)
# We create the schema
schema = JsonSchema(TypeUtilities.pyToGson(schema_file))
# Validation of the message with the schema
errors = JsonValidator.validate(schema, message_json)
if (len(errors) != 0):
return_message = (
"Error in the schema validation. "
+ "Check that the format follows the schema ("
+ schema_path.split(os.sep)[-1]
+ "). "
+ str(errors)
)
return False, return_message
# Everything goes right
result = True
return_message = ""
except:
filename = str(sys._getframe().f_code.co_filename)
func = str(sys._getframe().f_code.co_name),
line = str(sys._getframe().f_lineno)
exception_message = get_exception_message(filename, func, line)
system.util.getLogger("SGP.Core").fatal(exception_message)
result = False
return_message = exception_message
return result, return_message
Being brief, this code gets a JSON dict (param message) and validate it with the json schema located in the file path (param schema_path).
The message:
{'date': '2024-06-10 10:40:00.000+01:00'}
The schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "http://example.com/example.json",
"type": "object",
"properties": {
"date": {
"type": "string",
"format": "date-time"
}
}
}
The thing is, that I'm not able to parse the validation with this message:
{'date': '2024-06-10 10:40:00.000'}
As you may see, there is no time offset at the end. However, the schema speciffications say that it should be possible.
My question is: Is the Ignition code validation outdated or I'm doing something wrong? Also, is there any other way I could validate that type of date-format? I think that with pattern you can do it, but not sure if is the correct aproach.
Thanks in advance!
Any comments to improve my code are appreciated!
It's not allowed.
Founded this: RFC 3339 - Date and Time on the Internet: Timestamps
You can't put a date-time without the time offset.
I like the comments and spaced out code. That makes it easier to read, so good job on that!
I would add that you should probably specify the exceptions you're trying to catch. Even if you're trying to catch "everything", there are some errors you don't actually want to catch (certain interrupts, for example). The forums have more info on this somewhere if you're curious.
Most of my "just catch everything, idc" blocks look like this:
import java.lang.Exception as JavaException
try:
# code here...
except (Exception, JavaException) as ex:
# handle exceptions here...
or this:
import java.lang.Exception as JavaException
try:
# code here...
except Exception as pyEx:
# handle python exceptions here...
except JavaException as javaEx:
# handle java exceptions here...
I'd probably revisit the error handling, handling them through return values is... so C !
Exceptions are cool. You can just make your own and raise it with whatever parameters and message you want.
Try this for the basics:
class SchemaValidationError (Exception):
def __init__(self, *args, **kwargs):
self.errors = kwargs.pop('errors')
super(type(self), self).__init__(args, kwargs)
errors = [
'error1',
'error2'
]
try:
# note that you don't need to check the length. This will return True if errors is not empty, False otherwise
if errors:
raise SchemaValidationError(errors=errors)
except SchemaValidationError as e:
print e.errors
To reliably catch everything from Java, use java.lang.Throwable
instead of java.lang.Exception
.
Don't mix Java and Jython exceptions in same clause. They are fundamentally different class hierarchies and all but the most trivial uses require checking their types. Separate except
clauses avoids the need for if
statements to distinguish the exception type.
3 Likes
Thanks for the advice!
However, I don't quite understand why should I difference between Python and Java. I understand the point of handeling only some exceptions, or to differenciate some of them.
But, why between java and python?
We do it using return values because in other context we need the code to not stop. For example, in this case, even if we return return False, ["error1, error2, ...]
the above function that call this one needs to know that it returned "False".
Let me give you an example:
def fun1():
# function that returns a json
json = json_builder()
# original function from the post
validation = validate_schema(json, "C://schema.scm")
if not validation[0] # return = False
print validation[1] # error message
json = {"result":"KO"} # rebuild the json with error
# saves json in the bd
save_json(json)
return True
I'm not really sure how to do this with exceptios. Notice that I'm working between different functions from different files.
re raise the exception from the error handling in the called function, and in the calling function, just check for exceptions:
def some_func():
try:
something_that_could_raise_an_exception()
except SomeException as e:
handle_error()
raise
def calling_function():
try:
some_func()
except SomeException as e:
more_handling()
I'm not a huge fan of having try/excepts everywhere, but I do this when I want to handle the exception in one specific place, but still let the exception bubble up to the top most level, where it will be sent to the gateway logs. In this case, I catch the exception where I want to handle it ONLY, and then re raise it.
I prefer this over returning error codes and whatnot, because you can finely tune your error handling based on the exception type, and it doesn't pollute your return values.
Error handling is a tricky subject and I'm still trying to figure out how to do it in the best possible way, so take all this with a grain of salt and wait for other opinions/read up on the subject... But I'm pretty sure that moving away from C style error returns is a big step forward.
6 Likes