How to marshall/access data from within JSON::PullParser

Good Day,

Not sure the most appropriate method for handling this question, but scouring Google has yet to pay off.

I effectively have a PSQL query (using the “pg” shard) that builds a query in a rather dynamic fashion.
Now this is rather awful indeed to build the hash out in this fashion. However, the types of what is to be returned is entirely unknown. Basically I need to return some data from any table based off of the object_id and the model_name.

Thus I cannot build out a struct as I do not know the data types, therefore, this is the best I can come up with.

    def fetch_subject_if_exists(model_name, object_id)
      if object_id.not_nil! && model_name.not_nil!
        pg_db.query "SELECT * FROM #{model_name} WHERE id = '#{object_id}' LIMIT 1" do |data|

          dataHash = Hash(String, Array(PG::BoolArray) | Array(PG::CharArray) | Array(PG::Float32Array) | Array(PG::Float64Array) | Array(PG::Int16Array) | Array(PG::Int32Array) | Array(PG::Int64Array) | Array(PG::NumericArray) | Array(PG::StringArray) | Array(PG::TimeArray) | Bool | Char | Float32 | Float64 | Int16 | Int32 | Int64 | JSON::PullParser | PG::Geo::Box | PG::Geo::Circle | PG::Geo::Line | PG::Geo::LineSegment | PG::Geo::Path | PG::Geo::Point | PG::Geo::Polygon | PG::Interval | PG::Numeric | Slice(UInt8) | String | Time | UInt32 | UUID | Nil).new

          data.each {
            data.each_column { |c| dataHash.merge!({"#{c}" => data.read}) }
          }

          return dataHash
        end
      else
        return {} of Int32 => Int32
      end
    end

The primary issue here is that some of the values are of type JSON::PullParser when I want them to effectively be a JSON blob or Hash. I have read through the documentation here multiple times:
https://crystal-lang.org/api/1.2.2/JSON/PullParser.html

However, it is entirely unhelpful in providing any insights as to what might be a decent method of converting the type into something useful.

If I attempt to determine the type of the object within the data.each loop the following occurs:

          data.each {
              value = data.read

              if value.class == JSON::PullParser
                puts value.read_raw
              end
              
            data.each_column { |c| dataHash.merge!({"#{c}" => data.read}) }
          }

Effectively the compiler is treating every object of any of the types within the Hash starting with a Array(PG::BoolArray) which obviously does not have the methods of a JSON::PullParser object.

So the problem seems multi-fold. With the abstract/random data types I will get back from the query above I cannot build something like a struct to contain the data. Additionally, using the above to build a hash of the data I do get back, I cannot do anything with the JSON::PullParser objects in order to get them into something usable.

Any assistance is most greatly appreciated. Now this is the first time I have written any code in Crystal (Starting Friday) and I am at an impasse here as well as lacking knowledge.

Thus, I am very open to better ways of doing this.

Cheers!

But if each model has its own table, then wouldn’t you inherently know the data types for each model? Then you’d be able to make use of DB::Serializable - db 0.10.1.

EDIT: Or is more like you have a TEXT column that could be a string, integer, or JSON?

Do you have an example of when this is true? Out of curiosity, when you’re doing puts value.read_raw does this still print if you do like value.is_a? JSON::PullParser?

Regardless without knowing more about why this needs to be as dynamic, my only suggestion would to be not to do that. Otherwise dealing with a with a large union like that is going to be a nightmare.