smithy-json 1.0.0.pre1
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 +7 -0
- data/CHANGELOG.md +4 -0
- data/VERSION +1 -0
- data/lib/smithy-json/codec.rb +31 -0
- data/lib/smithy-json/deserializer.rb +120 -0
- data/lib/smithy-json/json_engine.rb +22 -0
- data/lib/smithy-json/oj_engine.rb +22 -0
- data/lib/smithy-json/serializer.rb +114 -0
- data/lib/smithy-json.rb +79 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7d10d334459b5725b2db3bff1f85606f48557787d60c130b850e1aef10c77b3d
|
4
|
+
data.tar.gz: 43dde2a55c584ebfb576489bd654a03d8c92e36f13fddc7acabcf1ca313009bc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 15f40462768f4e294e86b382ca91adfbab7f038c335f65a2df76a60fd143434d64a64d4daadf574bc294bd5f37b338988ff216983289d87566bc4ed4d85e1e97
|
7
|
+
data.tar.gz: 2e85d7bfa40a407212592cf6df0404188b2e83ca751781f6e4873ebbf99750e063f457746b9942e1f5ada9a6ddb8a3077bd8394b16c23370025b09e77000b805
|
data/CHANGELOG.md
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0.pre1
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'deserializer'
|
4
|
+
require_relative 'serializer'
|
5
|
+
|
6
|
+
module Smithy
|
7
|
+
module JSON
|
8
|
+
# @api private
|
9
|
+
class Codec
|
10
|
+
# @param [Hash] options
|
11
|
+
def initialize(options = {})
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Shape] shape
|
16
|
+
# @param [Object] data
|
17
|
+
# @return [String, nil]
|
18
|
+
def serialize(shape, data)
|
19
|
+
Serializer.new(@options).serialize(shape, data)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Shape] shape
|
23
|
+
# @param [String] bytes
|
24
|
+
# @param [Struct] target
|
25
|
+
# @return [Object, nil]
|
26
|
+
def deserialize(shape, bytes, target = nil)
|
27
|
+
Deserializer.new(@options).deserialize(shape, bytes, target)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module Smithy
|
6
|
+
module JSON
|
7
|
+
# @api private
|
8
|
+
class Deserializer
|
9
|
+
include Smithy::Schema::Shapes
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@json_name = options[:json_name] || false
|
13
|
+
end
|
14
|
+
|
15
|
+
def deserialize(shape, bytes, target)
|
16
|
+
return {} if bytes.empty?
|
17
|
+
|
18
|
+
ref = shape.is_a?(ShapeRef) ? shape : ShapeRef.new(shape: shape)
|
19
|
+
shape(ref, Smithy::JSON.load(bytes), target)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def shape(ref, value, target = nil) # rubocop:disable Metrics/CyclomaticComplexity
|
25
|
+
case ref.shape
|
26
|
+
when BlobShape then Base64.decode64(value)
|
27
|
+
when FloatShape then float(value)
|
28
|
+
when ListShape then list(ref, value, target)
|
29
|
+
when MapShape then map(ref, value, target)
|
30
|
+
when StructureShape then structure(ref, value, target)
|
31
|
+
when TimestampShape then timestamp(value)
|
32
|
+
when UnionShape then union(ref, value, target)
|
33
|
+
else value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def float(value)
|
38
|
+
case value
|
39
|
+
when 'Infinity' then ::Float::INFINITY
|
40
|
+
when '-Infinity' then -::Float::INFINITY
|
41
|
+
when 'NaN' then ::Float::NAN
|
42
|
+
when nil then nil
|
43
|
+
else value.to_f
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def list(ref, values, target = nil)
|
48
|
+
return if values.nil?
|
49
|
+
|
50
|
+
target = [] if target.nil?
|
51
|
+
values.each do |value|
|
52
|
+
next if value.nil? && !sparse?(ref.shape)
|
53
|
+
|
54
|
+
target << shape(ref.shape.member, value)
|
55
|
+
end
|
56
|
+
target
|
57
|
+
end
|
58
|
+
|
59
|
+
def map(ref, values, target = nil)
|
60
|
+
target = {} if target.nil?
|
61
|
+
values.each do |key, value|
|
62
|
+
next if value.nil? && !sparse?(ref.shape)
|
63
|
+
|
64
|
+
target[key] = shape(ref.shape.value, value)
|
65
|
+
end
|
66
|
+
target
|
67
|
+
end
|
68
|
+
|
69
|
+
def structure(ref, values, target = nil)
|
70
|
+
return if values.nil?
|
71
|
+
|
72
|
+
target = ref.shape.type.new if target.nil?
|
73
|
+
ref.shape.members.each do |member_name, member_ref|
|
74
|
+
value = values[location_name(member_ref)]
|
75
|
+
target[member_name] = shape(member_ref, value) unless value.nil?
|
76
|
+
end
|
77
|
+
target
|
78
|
+
end
|
79
|
+
|
80
|
+
def timestamp(value)
|
81
|
+
case value
|
82
|
+
when nil then nil
|
83
|
+
when Numeric then Time.at(value)
|
84
|
+
when /^[\d.]+$/ then Time.at(value.to_f)
|
85
|
+
else
|
86
|
+
begin
|
87
|
+
fractional_time = Time.parse(value).to_f
|
88
|
+
Time.at(fractional_time).utc
|
89
|
+
rescue ArgumentError
|
90
|
+
raise "unhandled timestamp format: #{value}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def union(ref, values, target = nil) # rubocop:disable Metrics/AbcSize
|
96
|
+
ref.shape.members.each do |member_name, member_ref|
|
97
|
+
value = values[location_name(member_ref)]
|
98
|
+
next if value.nil?
|
99
|
+
|
100
|
+
target = ref.shape.member_type(member_name) if target.nil?
|
101
|
+
return target.new(member_name => shape(member_ref, value))
|
102
|
+
end
|
103
|
+
|
104
|
+
values.delete('__type')
|
105
|
+
key, value = values.first
|
106
|
+
ref.shape.member_type(:unknown).new(unknown: { key => value })
|
107
|
+
end
|
108
|
+
|
109
|
+
def location_name(ref)
|
110
|
+
return ref.member_name unless @json_name
|
111
|
+
|
112
|
+
ref.traits['smithy.api#jsonName'] || ref.member_name
|
113
|
+
end
|
114
|
+
|
115
|
+
def sparse?(shape)
|
116
|
+
shape.traits.key?('smithy.api#sparse')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Smithy
|
6
|
+
module JSON
|
7
|
+
# @api private
|
8
|
+
module JsonEngine
|
9
|
+
class << self
|
10
|
+
def load(json)
|
11
|
+
::JSON.parse(json)
|
12
|
+
rescue ::JSON::ParserError => e
|
13
|
+
raise ParseError, e
|
14
|
+
end
|
15
|
+
|
16
|
+
def dump(value)
|
17
|
+
::JSON.dump(value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'oj'
|
4
|
+
|
5
|
+
module Smithy
|
6
|
+
module JSON
|
7
|
+
# @api private
|
8
|
+
module OjEngine
|
9
|
+
class << self
|
10
|
+
def load(json)
|
11
|
+
Oj.load(json, mode: :compat, symbol_keys: false)
|
12
|
+
rescue Oj::ParseError, EncodingError => e
|
13
|
+
raise ParseError, e
|
14
|
+
end
|
15
|
+
|
16
|
+
def dump(value)
|
17
|
+
Oj.dump(value, mode: :compat)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module Smithy
|
6
|
+
module JSON
|
7
|
+
# @api private
|
8
|
+
class Serializer
|
9
|
+
include Smithy::Schema::Shapes
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@json_name = options[:json_name] || false
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize(shape, data)
|
16
|
+
ref = shape.is_a?(ShapeRef) ? shape : ShapeRef.new(shape: shape)
|
17
|
+
Smithy::JSON.dump(shape(ref, data))
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def shape(ref, value) # rubocop:disable Metrics/CyclomaticComplexity
|
23
|
+
shape = ref.shape
|
24
|
+
case shape
|
25
|
+
when BlobShape then blob(value)
|
26
|
+
when FloatShape then float(value)
|
27
|
+
when ListShape then list(ref, value)
|
28
|
+
when MapShape then map(ref, value)
|
29
|
+
when StructureShape then structure(ref, value)
|
30
|
+
when TimestampShape then timestamp(ref, value)
|
31
|
+
when UnionShape then union(ref, value)
|
32
|
+
else value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def blob(value)
|
37
|
+
Base64.strict_encode64(value.respond_to?(:read) ? value.read : value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def float(value)
|
41
|
+
if value == ::Float::INFINITY
|
42
|
+
'Infinity'
|
43
|
+
elsif value == -::Float::INFINITY
|
44
|
+
'-Infinity'
|
45
|
+
elsif value.nan?
|
46
|
+
'NaN'
|
47
|
+
else
|
48
|
+
value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def list(ref, values)
|
53
|
+
return if values.nil?
|
54
|
+
|
55
|
+
shape = ref.shape
|
56
|
+
values.collect do |value|
|
57
|
+
shape(shape.member, value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def map(ref, values)
|
62
|
+
return if values.nil?
|
63
|
+
|
64
|
+
shape = ref.shape
|
65
|
+
values.each.with_object({}) do |(key, value), data|
|
66
|
+
data[key] = shape(shape.value, value)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def structure(ref, values)
|
71
|
+
return if values.nil?
|
72
|
+
|
73
|
+
ref.shape.members.each_with_object({}) do |(member_name, member_ref), data|
|
74
|
+
value = values[member_name]
|
75
|
+
data[location_name(member_ref)] = shape(member_ref, value) unless value.nil?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def timestamp(ref, value)
|
80
|
+
trait = 'smithy.api#timestampFormat'
|
81
|
+
case ref.traits[trait] || ref.shape.traits[trait]
|
82
|
+
when 'date-time' then value.utc.iso8601
|
83
|
+
when 'http-date' then value.utc.httpdate
|
84
|
+
else
|
85
|
+
# default to epoch-seconds
|
86
|
+
value.to_i
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def union(ref, values) # rubocop:disable Metrics/AbcSize
|
91
|
+
return if values.nil?
|
92
|
+
|
93
|
+
data = {}
|
94
|
+
if values.is_a?(Schema::Union)
|
95
|
+
_name, member_ref = ref.shape.member_by_type(values.class)
|
96
|
+
data[location_name(member_ref)] = shape(member_ref, values.value)
|
97
|
+
else
|
98
|
+
key, value = values.first
|
99
|
+
if ref.shape.member?(key)
|
100
|
+
member_ref = ref.shape.member(key)
|
101
|
+
data[location_name(member_ref)] = shape(member_ref, value)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
data
|
105
|
+
end
|
106
|
+
|
107
|
+
def location_name(ref)
|
108
|
+
return ref.member_name unless @json_name
|
109
|
+
|
110
|
+
ref.traits['smithy.api#jsonName'] || ref.member_name
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/smithy-json.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'smithy-schema'
|
4
|
+
|
5
|
+
require_relative 'smithy-json/codec'
|
6
|
+
|
7
|
+
module Smithy
|
8
|
+
# Smithy::JSON is a purpose-built set of utilities for working with JSON.
|
9
|
+
module JSON
|
10
|
+
VERSION = File.read(File.expand_path('../VERSION', __dir__.to_s)).strip
|
11
|
+
|
12
|
+
# Raised when a JSON parsing error occurs.
|
13
|
+
class ParseError < StandardError
|
14
|
+
def initialize(error)
|
15
|
+
@error = error
|
16
|
+
super(error.message)
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :error
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# @param [Symbol,Class] engine
|
24
|
+
# Must be one of the following values:
|
25
|
+
#
|
26
|
+
# * :oj
|
27
|
+
# * :json
|
28
|
+
#
|
29
|
+
def engine=(engine)
|
30
|
+
@engine = engine.is_a?(Class) ? engine : load_engine(engine)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Class] Returns the default engine.
|
34
|
+
# One of:
|
35
|
+
#
|
36
|
+
# * {OjEngine}
|
37
|
+
# * {JsonEngine}
|
38
|
+
#
|
39
|
+
def engine
|
40
|
+
set_default_engine unless @engine
|
41
|
+
@engine
|
42
|
+
end
|
43
|
+
|
44
|
+
def load(json)
|
45
|
+
@engine.load(json)
|
46
|
+
end
|
47
|
+
|
48
|
+
def dump(value)
|
49
|
+
@engine.dump(value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_default_engine
|
53
|
+
%i[oj json].each do |name|
|
54
|
+
@engine ||= try_load_engine(name)
|
55
|
+
end
|
56
|
+
return if @engine
|
57
|
+
|
58
|
+
raise 'Unable to find a compatible json library. ' \
|
59
|
+
'Ensure that you have installed or added to your Gemfile one of: oj or json'
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def try_load_engine(name)
|
65
|
+
load_engine(name)
|
66
|
+
rescue LoadError
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def load_engine(name)
|
71
|
+
require "smithy-json/#{name}_engine"
|
72
|
+
const_name = "#{name[0].upcase}#{name[1..]}Engine"
|
73
|
+
const_get(const_name)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
set_default_engine
|
78
|
+
end
|
79
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smithy-json
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.pre1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Amazon Web Services
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-06-26 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: base64
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: smithy-schema
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - '='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0.pre1
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - '='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.0.0.pre1
|
40
|
+
description: Smithy is a code generation toolkit for creating Client and Server SDKs
|
41
|
+
from Smithy models.
|
42
|
+
executables: []
|
43
|
+
extensions: []
|
44
|
+
extra_rdoc_files: []
|
45
|
+
files:
|
46
|
+
- CHANGELOG.md
|
47
|
+
- VERSION
|
48
|
+
- lib/smithy-json.rb
|
49
|
+
- lib/smithy-json/codec.rb
|
50
|
+
- lib/smithy-json/deserializer.rb
|
51
|
+
- lib/smithy-json/json_engine.rb
|
52
|
+
- lib/smithy-json/oj_engine.rb
|
53
|
+
- lib/smithy-json/serializer.rb
|
54
|
+
homepage: https://github.com/smithy-lang/smithy-ruby
|
55
|
+
licenses:
|
56
|
+
- Apache-2.0
|
57
|
+
metadata: {}
|
58
|
+
rdoc_options: []
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '3.3'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
requirements: []
|
72
|
+
rubygems_version: 3.6.2
|
73
|
+
specification_version: 4
|
74
|
+
summary: JSON utilities and codec for Smithy generated clients and servers
|
75
|
+
test_files: []
|