What kind (types) of data ?
To do what with ?
How will the user choose to edit them ?
As John mentioned, there is the standard library JSON, which I think SketchUp loads in most cases before any plugins are processed. But you always should show a dependency if using it at the top of your extension module:
module Author::SomePlugin
require "json"
# ...
end
However, neither the Sketchup::AttributeDictionary
class, nor any of the other API entity classes, are implemented to properly interface with the JSON or Marshalling libraries.
Sketchup::AttributeDictionary.json_creatable?
#=> false
… meaning the class does not define a .json_create()
class method.
The Sketchup::AttributeDictionary
class gets the .to_json
instance method from inheritance via Object
(which gets it from the json mixin library.)
But the Sketchup::AttributeDictionary
class has (so far) failed to properly define .inspect
and .to_json
method overrides, so that Sketchup::AttributeDictionary#to_json()
will work.
# With a DC selected, and "cdef" set to reference it's definition object:
ad = cdef.attribute_dictionaries["dynamic_attributes"]
#=> #<Sketchup::AttributeDictionary:0x0000000ea1c378>
j = ad.to_json
#=> "#<Sketchup::AttributeDictionary:0x0000000ea1c378>"
Normally you can use the JSON::generate(hash_object)
module function workaround.
But be aware, as of SketchUp 2016, the Sketchup::AttributeDictionary
class has not provided .to_h()
or .to_hash()
conversion methods, and neither has the Ruby 2.0 core.
The Ruby 2.2 core (SketchUp 2017+) added an inheritable .to_h()
conversion method that can be inherited from the Enumerable
mixin module, (which is already mixed into Sketchup::AttributeDictionary
and many other API collection type classes.)
But, I haven’t yet done much SketchUp 2017 testing so test if Ruby 2.2.4’s .to_h()
mixin works for the dictionary class. If not you’ll need to go the singleton route, below, but omit the response test in favor of a SketchUp version test (or some other way to determine if this method is working correctly.)
You can add singleton .to_h()
methods to your specific plugin dictionaries, “on the fly” like:
if !dict.respond_to(:to_h)
def dict.to_h
Hash[self.entries] # Hash class alternate constructor.
# Use Enumerable#entries(), which creates an array argument,
# of nested arrays, each with a key / value pair.
end
end
Then, you can use:
json_string = dict.to_h.to_json
… because Hash
’s to_json
will work, in Ruby 2.0 and higher if the JSON library is loaded.
In the same way, SketchUp entity objects have not been made compatible with the core Marshal
module. None of the SketchUp API entity classes implement the proper marshalling conversion methods requisite to be compatible with this library, which are marshall_dump()
and marshall_load()
.
In addition marshalling complicates matters with byte stream versioning compatibility issues that are probably best avoided. (See the second paragraph of the Marshal
module documentation.)
If this data will only be used in a choice control that the user will pick from (ie, a list in a dialog box,) then it maks no sense to bloat the model filesize by attaching an entire library to a model entity.
A simple Hash
of data records, accessed by a (String or Symbol) key, will suffice (ie, tabular data.)
The kind of object used for the data records is debatable:
- If you only wish to access each data field by numerical index, then each record can be an
Array
.
- If you wish to access each data field by a key name, then each record can be an
Hash
.
- If you wish to access each data field by a field name instance method (ie, dot notation,) then each record can be an
OpenStruct
.
So I’d recommend using a Hash of record OpenStructs (or Hashes.)
And then just save them as JSON strings using Ruby core File
class.
See JSON lib’s additions to OpenStruct class:
# USE (assumses inside a module that extends itself):
dir = File.dirname(__FILE__) # your plugin subdirectory
PATH ||= {} # Hash of paths used in plugin
PATH[:truss_data]= File.join( dir, "data", "truss_data.json" )
@trussjson = get_datajson(PATH[:truss_data])
# You may need to pass this json to a WebDialog or HTMLDialog
@trussdata = get_datahash(@trussjson)
# For internal Ruby building geometry etc.
# This can be set immediately after getting from JSON file,
# or later from the WebDIalog if changes have been made.
# After making changes to @trussdata hash:
save_datajson(PATH[:truss_data],@trussdata)
def save_datajson(filepath,datahash)
# Always wrap IO ops within a rescue block:
File.open(filepath, "w") {|f| f.write(datahash.to_json) }
rescue
# handle IO errors here
else
# set a success flag ?
end
def get_datajson(filepath)
# Always wrap IO ops within a rescue block:
File.read(filepath, "r")
rescue
# handle IO errors here
else
# set a success flag ?
end
def get_datahash(json_str)
JSON.parse(json_str)
rescue
# handle parse errors here
else
# set a success flag ?
end