Skip to content

Make json, cbor, xml, html enforce data types #1006

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 32 additions & 29 deletions openc3/lib/openc3/accessors/accessor.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# encoding: ascii-8bit

# Copyright 2022 OpenC3, Inc.
# Copyright 2023 OpenC3, Inc.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
Expand All @@ -16,6 +16,8 @@
# This file may also be used under the terms of a commercial license
# if purchased from OpenC3, Inc.

require 'json'

module OpenC3
class Accessor
attr_accessor :packet
Expand Down Expand Up @@ -64,15 +66,15 @@ def enforce_short_buffer_allowed
return false
end

def enforce_derived_write_conversion(item)
def enforce_derived_write_conversion(_item)
return true
end

def self.read_item(item, buffer)
def self.read_item(_item, _buffer)
raise "Must be defined by subclass"
end

def self.write_item(item, value, buffer)
def self.write_item(_item, _value, _buffer)
raise "Must be defined by subclass"
end

Expand All @@ -92,33 +94,34 @@ def self.write_items(items, values, buffer)
end

def self.convert_to_type(value, item)
data_type = item.data_type
if (data_type == :STRING) || (data_type == :BLOCK)
#######################################
# Handle :STRING and :BLOCK data types
#######################################
value = value.to_s

elsif (data_type == :INT) || (data_type == :UINT)
###################################
# Handle :INT data type
###################################
value = Integer(value)

elsif data_type == :FLOAT
##########################
# Handle :FLOAT data type
##########################
value = Float(value)

case item.data_type
when :OBJECT
# Do nothing for complex object types
when :STRING, :BLOCK
if item.array_size
value = JSON.parse(value) if value.is_a? String
value = value.map { |v| v.to_s }
else
value = value.to_s
end
when :UINT, :INT
if item.array_size
value = JSON.parse(value) if value.is_a? String
value = value.map { |v| Integer(v) }
else
value = Integer(value)
end
when :FLOAT
if item.array_size
value = JSON.parse(value) if value.is_a? String
value = value.map { |v| Float(v) }
else
value = Float(value)
end
else
############################
# Handle Unknown data types
############################

raise(ArgumentError, "data_type #{data_type} is not recognized")
raise(ArgumentError, "data_type #{item.data_type} is not recognized")
end
return value
end
end
end
end
6 changes: 4 additions & 2 deletions openc3/lib/openc3/accessors/json_accessor.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# encoding: ascii-8bit

# Copyright 2022 OpenC3, Inc.
# Copyright 2023 OpenC3, Inc.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
Expand All @@ -24,7 +24,8 @@ module OpenC3
class JsonAccessor < Accessor
def self.read_item(item, buffer)
return nil if item.data_type == :DERIVED
return JsonPath.on(buffer, item.key).first
value = JsonPath.on(buffer, item.key).first
return convert_to_type(value, item)
end

def self.write_item(item, value, buffer)
Expand Down Expand Up @@ -123,6 +124,7 @@ def self.write_item_internal(item, value, decoded)
raise "Unsupported key/token: #{item.key} - #{token}"
end
end
value = convert_to_type(value, item)
if parent_node
parent_node[parent_key] = value
else
Expand Down
56 changes: 31 additions & 25 deletions openc3/python/openc3/accessors/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# This file may also be used under the terms of a commercial license
# if purchased from OpenC3, Inc.

from ast import literal_eval


class Accessor:
def __init__(self, packet=None):
Expand Down Expand Up @@ -72,30 +74,34 @@ def class_write_items(cls, items, values, buffer):

@classmethod
def convert_to_type(cls, value, item):
data_type = item.data_type
if (data_type == "STRING") or (data_type == "BLOCK"):
#######################################
# Handle :STRING and :BLOCK data types
#######################################
value = str(value)

elif (data_type == "INT") or (data_type == "UINT"):
###################################
# Handle :INT data type
###################################
value = int(value)

elif data_type == "FLOAT":
##########################
# Handle :FLOAT data type
##########################
value = float(value)

else:
############################
# Handle Unknown data types
############################

raise AttributeError(f"data_type {data_type} is not recognized")
match item.data_type:
case "OBJECT":
pass # No conversion on complex OBJECT types
case "STRING" | "BLOCK":
if item.array_size:
if isinstance(value, str):
# Thought about using json.loads here but it doesn't
# support basic examples like "[2.2, '3', 4]"
# Seems it's pretty strict about quotes and escaping
value = literal_eval(value)
value = [str(x) for x in value]
else:
value = str(value)
case "INT" | "UINT":
if item.array_size:
if isinstance(value, str):
value = literal_eval(value)
value = [int(float(x)) for x in value]
else:
value = int(float(value))
case "FLOAT":
if item.array_size:
if isinstance(value, str):
value = literal_eval(value)
value = [float(x) for x in value]
else:
value = float(value)
case _:
raise AttributeError(f"data_type {item.data_type} is not recognized")

return value
3 changes: 2 additions & 1 deletion openc3/python/openc3/accessors/json_accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def class_read_item(cls, item, buffer):
if type(buffer) is bytearray:
buffer = json.loads(buffer.decode())
result = parse(item.key).find(buffer)
return result[0].value
return cls.convert_to_type(result[0].value, item)

@classmethod
def class_write_item(cls, item, value, buffer):
Expand All @@ -38,6 +38,7 @@ def class_write_item(cls, item, value, buffer):
else:
decoded = buffer

value = cls.convert_to_type(value, item)
result = parse(item.key).update(decoded, value)

if type(buffer) is bytearray:
Expand Down
Loading