json_struct_mapper 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1e1a4c44ea87b099135a38beb8be6cf25462334d112b117317ad735335e6f72b
4
+ data.tar.gz: f036202b6da95d30cfc4daf23b10719190a9422d3151e7e5bc847b735f98c6b5
5
+ SHA512:
6
+ metadata.gz: e366722a7e60c18cf09be666f5eb4017139d2880f242478e3ce76cb1571bd8ed26898d038e82357853923bdfc9317bfbff1007a68ab85e95d31f1a660d816a29
7
+ data.tar.gz: 982ba9aa76a859dd21ecf6ffbee6edcf4622d14b042dfbe544ed03edae46e2aeda1f4221b3ac0efe9c8e35e78a442de9b56502cdbe67ebafb9b686d5dbc2bf86
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Uppala Shivani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # JsonStructMapper
2
+
3
+ JsonStructMapper provides an easy way to convert JSON files and hashes into Ruby Struct objects, with support for nested structures and arrays. It also supports converting Structs back to hashes.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'json_struct_mapper'
11
+ ```
12
+ And then execute:
13
+ ```ruby
14
+ $ bundle install
15
+ ```
16
+ Or install it yourself as:
17
+ ```ruby
18
+ $ gem install json_struct_mapper
19
+ ```
20
+ ## Usage
21
+ ### From a JSON file
22
+ ```ruby
23
+ # Load entire JSON file
24
+ converter = JsonStructMapper.from_file('data.json')
25
+
26
+ # Load specific key from JSON file
27
+ converter = JsonStructMapper.from_file('data.json', 'users')
28
+
29
+ # Access data via struct
30
+ converter.object.name
31
+ converter.object.email
32
+ ```
33
+ ### From a Hash
34
+ ```ruby
35
+ hash = { name: 'John', age: 30, address: { city: 'NYC' } }
36
+ converter = JsonStructMapper.from_hash(hash)
37
+
38
+ converter.object.name # => 'John'
39
+ converter.object.address.city # => 'NYC'
40
+ ```
41
+ ### From a JSON string
42
+ ```ruby
43
+ json_string = '{"name": "John", "age": 30}'
44
+ converter = JsonStructMapper.from_json(json_string)
45
+
46
+ converter.object.name # => 'John'
47
+ ```
48
+ ### Convert back to Hash or JSON
49
+ ```ruby
50
+ # Convert to hash (removes nil values by default)
51
+ hash = converter.to_hash
52
+
53
+ # Keep nil values
54
+ hash = converter.to_hash(compact: false)
55
+
56
+ # Convert to JSON string
57
+ json = converter.to_json
58
+
59
+ # Pretty print JSON
60
+ json = converter.to_json(pretty: true)
61
+ ```
62
+ ### Handling nested structures
63
+ ```ruby
64
+ json = {
65
+ user: {
66
+ name: 'John',
67
+ contacts: [
68
+ { type: 'email', value: 'john@example.com' },
69
+ { type: 'phone', value: '123-456-7890' }
70
+ ]
71
+ }
72
+ }
73
+
74
+ converter = JsonStructMapper.from_hash(json)
75
+ converter.object.user.contacts[0].value # => 'john@example.com'
76
+ ```
77
+ ## Error Handling
78
+ The gem provides specific error classes:
79
+ 1. JsonStructMapper::FileNotFoundError - When file doesn't exist
80
+ 2. JsonStructMapper::InvalidJSONError - When JSON is malformed
81
+ 3. JsonStructMapper::InvalidKeyError - When specified key doesn't exist
82
+
83
+ ```ruby
84
+ begin
85
+ converter = JsonStructMapper.from_file('missing.json')
86
+ rescue JsonStructMapper::FileNotFoundError => e
87
+ puts "File error: #{e.message}"
88
+ end
89
+ ```
90
+
91
+ ## Development
92
+ After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
93
+ To install this gem onto your local machine, run bundle exec rake install.
94
+
95
+ ## Contributing
96
+ Bug reports and pull requests are welcome on GitHub at https://github.com/UppalaShivani/json_struct_mapper.
97
+
98
+ ## License
99
+ The gem is available as open source under the terms of the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+ RuboCop::RakeTask.new
9
+
10
+ task default: %i[spec rubocop]
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/json_struct_mapper/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'json_struct_mapper'
7
+ spec.version = JsonStructMapper::VERSION
8
+ spec.authors = ['Shivani Uppala', 'Sumit Ghosh']
9
+ spec.email = ['uppalashivani@gmail.com']
10
+
11
+ spec.summary = 'Convert JSON data into Ruby Struct objects with bidirectional mapping'
12
+ spec.description = 'JsonStructMapper provides an easy way to convert JSON files and hashes into Ruby Struct objects,
13
+ with support for nested structures and arrays. It also supports converting Structs
14
+ back to hashes.'
15
+ spec.homepage = 'https://github.com/UppalaShivani/json_struct_mapper/blob/main/README.md'
16
+ spec.license = 'MIT'
17
+ spec.required_ruby_version = '>= 2.7.0'
18
+
19
+ spec.metadata['homepage_uri'] = spec.homepage
20
+ spec.metadata['source_code_uri'] = 'https://github.com/UppalaShivani/json_struct_mapper'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (File.expand_path(f) == __FILE__) ||
26
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
27
+ end
28
+ end
29
+ spec.bindir = 'exe'
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ # Dependencies
34
+ spec.add_dependency 'json', '~> 2.0'
35
+
36
+ # Development dependencies
37
+ spec.add_development_dependency 'rake', '~> 13.0'
38
+ spec.add_development_dependency 'rspec', '~> 3.0'
39
+ spec.add_development_dependency 'rubocop', '~> 1.21'
40
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module JsonStructMapper
6
+ # Converter class handles the conversion between JSON/Hash and Struct objects
7
+ class Converter
8
+ attr_reader :object
9
+
10
+ # Initialize with a file path and optional key to extract from JSON
11
+ #
12
+ # @param file_path [String] Path to the JSON file
13
+ # @param key [String, nil] Optional key to extract from the JSON root
14
+ # @raise [FileNotFoundError] if file doesn't exist
15
+ # @raise [InvalidJSONError] if JSON is malformed
16
+ # @raise [InvalidKeyError] if specified key doesn't exist
17
+ def initialize(file_path, key = nil)
18
+ @object = load_from_file(file_path, key)
19
+ end
20
+
21
+ # Create a Converter from a hash directly
22
+ #
23
+ # @param hash [Hash] Hash to convert to struct
24
+ # @return [Converter] new instance
25
+ def self.from_hash(hash)
26
+ instance = allocate
27
+ instance.instance_variable_set(:@object, instance.send(:hash_to_struct, hash))
28
+ instance
29
+ end
30
+
31
+ # Create a Converter from a JSON string
32
+ #
33
+ # @param json_string [String] JSON string to parse and convert
34
+ # @param key [String, nil] Optional key to extract from the JSON root
35
+ # @return [Converter] new instance
36
+ def self.from_json(json_string, key = nil)
37
+ data_hash = JSON.parse(json_string)
38
+ hash = key ? data_hash[key] : data_hash
39
+ raise InvalidKeyError, "Key '#{key}' not found in JSON" if key && hash.nil?
40
+
41
+ from_hash(hash)
42
+ rescue JSON::ParserError => e
43
+ raise InvalidJSONError, "Failed to parse JSON: #{e.message}"
44
+ end
45
+
46
+ # Convert the struct object back to a hash
47
+ #
48
+ # @param compact [Boolean] whether to remove nil values
49
+ # @return [Hash] converted hash
50
+ def to_hash(compact: true)
51
+ hash = struct_to_hash(@object)
52
+ compact ? compact_hash(hash) : hash
53
+ end
54
+
55
+ # Convert the struct object to JSON
56
+ #
57
+ # @param pretty [Boolean] whether to format with indentation
58
+ # @return [String] JSON string
59
+ def to_json(pretty: false)
60
+ hash = to_hash
61
+ pretty ? JSON.pretty_generate(hash) : JSON.generate(hash)
62
+ end
63
+
64
+ private
65
+
66
+ def load_from_file(file_path, key)
67
+ raise FileNotFoundError, "File not found: #{file_path}" unless File.exist?(file_path)
68
+
69
+ json_data = File.read(file_path)
70
+ data_hash = JSON.parse(json_data)
71
+
72
+ hash = key ? data_hash[key] : data_hash
73
+ raise InvalidKeyError, "Key '#{key}' not found in JSON" if key && hash.nil?
74
+
75
+ hash_to_struct(hash)
76
+ rescue JSON::ParserError => e
77
+ raise InvalidJSONError, "Failed to parse JSON: #{e.message}"
78
+ end
79
+
80
+ # Recursively converts a hash to a Struct
81
+ #
82
+ # @param hash [Hash] hash to convert
83
+ # @return [Struct] converted struct
84
+ def hash_to_struct(hash)
85
+ return hash unless hash.is_a?(Hash)
86
+
87
+ struct_class = Struct.new(*hash.keys.map(&:to_sym))
88
+ struct_class.new(*hash.values.map { |value| convert_value_to_struct(value) })
89
+ end
90
+
91
+ # Convert individual values (handles Hash, Array, and primitives)
92
+ def convert_value_to_struct(value)
93
+ case value
94
+ when Hash
95
+ hash_to_struct(value)
96
+ when Array
97
+ value.map { |v| convert_value_to_struct(v) }
98
+ else
99
+ value
100
+ end
101
+ end
102
+
103
+ # Converts a Struct back to a hash
104
+ #
105
+ # @param struct [Struct] struct to convert
106
+ # @return [Hash] converted hash
107
+ def struct_to_hash(struct)
108
+ return struct unless struct.is_a?(Struct)
109
+
110
+ struct.to_h.transform_values { |value| convert_value_to_hash(value) }
111
+ end
112
+
113
+ # Convert individual values back (handles Struct, Array, and primitives)
114
+ def convert_value_to_hash(value)
115
+ case value
116
+ when Struct
117
+ struct_to_hash(value)
118
+ when Array
119
+ value.map { |v| convert_value_to_hash(v) }
120
+ else
121
+ value
122
+ end
123
+ end
124
+
125
+ # Recursively removes nil values from a hash
126
+ #
127
+ # @param hash [Hash] hash to compact
128
+ # @return [Hash] compacted hash
129
+ def compact_hash(hash)
130
+ hash.each_with_object({}) do |(k, v), result|
131
+ next if v.nil?
132
+
133
+ result[k] = v.is_a?(Hash) ? compact_hash(v) : v
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonStructMapper
4
+ class Error < StandardError; end
5
+ class FileNotFoundError < Error; end
6
+ class InvalidJSONError < Error; end
7
+ class InvalidKeyError < Error; end
8
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonStructMapper
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'json_struct_mapper/version'
4
+ require_relative 'json_struct_mapper/errors'
5
+ require_relative 'json_struct_mapper/converter'
6
+
7
+ # JsonStructMapper provides an easy way to convert JSON data into Ruby Struct objects
8
+ module JsonStructMapper
9
+ class << self
10
+ # Convenience method to create a converter from a file
11
+ def from_file(file_path, key = nil)
12
+ Converter.new(file_path, key)
13
+ end
14
+
15
+ # Convenience method to create a converter from a hash
16
+ def from_hash(hash)
17
+ Converter.from_hash(hash)
18
+ end
19
+
20
+ # Convenience method to create a converter from a JSON string
21
+ def from_json(json_string, key = nil)
22
+ Converter.from_json(json_string, key)
23
+ end
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json_struct_mapper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Shivani Uppala
8
+ - Sumit Ghosh
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-11-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.21'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.21'
69
+ description: |-
70
+ JsonStructMapper provides an easy way to convert JSON files and hashes into Ruby Struct objects,
71
+ with support for nested structures and arrays. It also supports converting Structs
72
+ back to hashes.
73
+ email:
74
+ - uppalashivani@gmail.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - json_struct_mapper.gemspec
83
+ - lib/json_struct_mapper.rb
84
+ - lib/json_struct_mapper/converter.rb
85
+ - lib/json_struct_mapper/errors.rb
86
+ - lib/json_struct_mapper/version.rb
87
+ homepage: https://github.com/UppalaShivani/json_struct_mapper/blob/main/README.md
88
+ licenses:
89
+ - MIT
90
+ metadata:
91
+ homepage_uri: https://github.com/UppalaShivani/json_struct_mapper/blob/main/README.md
92
+ source_code_uri: https://github.com/UppalaShivani/json_struct_mapper
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: 2.7.0
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubygems_version: 3.6.5
108
+ specification_version: 4
109
+ summary: Convert JSON data into Ruby Struct objects with bidirectional mapping
110
+ test_files: []