# オブジェクトをJSONとしてファイルに保存
File.open("data.json", "w") do |f|
JSON.dump({ name: "田中", age: 30 }, f)
end
# ファイルからJSONを読み込み
data = File.open("data.json") do |f|
JSON.load(f)
end
# オブジェクトをJSONとしてファイルに保存
File.open("data.json", "w") do |f|
JSON.dump({ name: "田中", age: 30 }, f)
end
# ファイルからJSONを読み込み
data = File.open("data.json") do |f|
JSON.load(f)
end
class User
def initialize(name, age)
@name = name
@age = age
end
# カスタムto_json実装
def to_json(*args)
{
name: @name,
age: @age
}.to_json(*args)
end
end
user = User.new("山田太郎", 30)
puts user.to_json # => {"name":"山田太郎","age":30}
class User
def initialize(name, age)
@name = name
@age = age
end
# カスタムto_json実装
def to_json(*args)
{
name: @name,
age: @age
}.to_json(*args)
end
end
user = User.new("山田太郎", 30)
puts user.to_json # => {"name":"山田太郎","age":30}
# ネストされた構造を再帰的に処理する
def process_nested_json(data)
case data
when Hash
data.transform_values { |v| process_nested_json(v) }
when Array
data.map { |item| process_nested_json(item) }
else
data
end
end
# 使用例:全ての文字列を大文字に変換
def upcase_strings(data)
process_nested_json(data) do |value|
value.is_a?(String) ? value.upcase : value
end
end
# ネストされた構造を再帰的に処理する
def process_nested_json(data)
case data
when Hash
data.transform_values { |v| process_nested_json(v) }
when Array
data.map { |item| process_nested_json(item) }
else
data
end
end
# 使用例:全ての文字列を大文字に変換
def upcase_strings(data)
process_nested_json(data) do |value|
value.is_a?(String) ? value.upcase : value
end
end
# 配列データの検証と変換
class ArrayProcessor
def initialize(json_array)
@data = JSON.parse(json_array)
end
# 必須フィールドの検証
def validate_required_fields(*fields)
@data.all? do |item|
fields.all? { |field| item.key?(field) && !item[field].nil? }
end
end
# 特定条件での絞り込み
def filter_by_condition(&block)
@data.select(&block)
end
# 構造の変換
def transform_structure
@data.map do |item|
{
id: item["id"],
details: item.except("id")
}
end
end
end
# 配列データの検証と変換
class ArrayProcessor
def initialize(json_array)
@data = JSON.parse(json_array)
end
# 必須フィールドの検証
def validate_required_fields(*fields)
@data.all? do |item|
fields.all? { |field| item.key?(field) && !item[field].nil? }
end
end
# 特定条件での絞り込み
def filter_by_condition(&block)
@data.select(&block)
end
# 構造の変換
def transform_structure
@data.map do |item|
{
id: item["id"],
details: item.except("id")
}
end
end
end
module JSONProcessor
class ValidationError < StandardError; end
class InvalidFormatError < StandardError; end
def self.parse_with_validation(json_string, required_fields: [])
begin
data = JSON.parse(json_string)
# 必須フィールドの検証
missing_fields = required_fields - data.keys
unless missing_fields.empty?
raise ValidationError, "必須フィールドがありません: #{missing_fields.join(', ')}"
end
data
rescue JSON::ParserError => e
raise InvalidFormatError, "JSONフォーマットが不正です: #{e.message}"
end
end
end
# 使用例
begin
data = JSONProcessor.parse_with_validation(
'{"name": "田中"}',
required_fields: ['name', 'age']
)
rescue JSONProcessor::ValidationError => e
puts "バリデーションエラー: #{e.message}"
rescue JSONProcessor::InvalidFormatError => e
puts "フォーマットエラー: #{e.message}"
end
module JSONProcessor
class ValidationError < StandardError; end
class InvalidFormatError < StandardError; end
def self.parse_with_validation(json_string, required_fields: [])
begin
data = JSON.parse(json_string)
# 必須フィールドの検証
missing_fields = required_fields - data.keys
unless missing_fields.empty?
raise ValidationError, "必須フィールドがありません: #{missing_fields.join(', ')}"
end
data
rescue JSON::ParserError => e
raise InvalidFormatError, "JSONフォーマットが不正です: #{e.message}"
end
end
end
# 使用例
begin
data = JSONProcessor.parse_with_validation(
'{"name": "田中"}',
required_fields: ['name', 'age']
)
rescue JSONProcessor::ValidationError => e
puts "バリデーションエラー: #{e.message}"
rescue JSONProcessor::InvalidFormatError => e
puts "フォーマットエラー: #{e.message}"
end
class JSONSanitizer
def self.sanitize(data)
case data
when Hash
data.transform_values { |v| sanitize(v) }
when Array
data.map { |item| sanitize(item) }
when String
sanitize_string(data)
else
data
end
end
private
def self.sanitize_string(str)
# XSS対策
str.gsub(/<[^>]*>/, '')
.gsub(/javascript:/i, '')
.strip
end
end
# 使用例
dirty_json = '{"name": "<script>alert(1)</script>", "description": "javascript:alert(2)"}'
data = JSON.parse(dirty_json)
clean_data = JSONSanitizer.sanitize(data)
class JSONSanitizer
def self.sanitize(data)
case data
when Hash
data.transform_values { |v| sanitize(v) }
when Array
data.map { |item| sanitize(item) }
when String
sanitize_string(data)
else
data
end
end
private
def self.sanitize_string(str)
# XSS対策
str.gsub(/<[^>]*>/, '')
.gsub(/javascript:/i, '')
.strip
end
end
# 使用例
dirty_json = '{"name": "<script>alert(1)</script>", "description": "javascript:alert(2)"}'
data = JSON.parse(dirty_json)
clean_data = JSONSanitizer.sanitize(data)
module JSONDebugger
def self.analyze_json(json_string)
begin
# JSONの構造解析
data = JSON.parse(json_string)
{
valid: true,
structure: analyze_structure(data),
size: json_string.bytesize,
depth: calculate_depth(data)
}
rescue JSON::ParserError => e
{
valid: false,
error: e.message,
position: e.pos,
snippet: json_string[([e.pos - 20, 0].max)..(e.pos + 20)]
}
end
end
private
def self.analyze_structure(data, depth = 0)
case data
when Hash
"Hash with #{data.keys.size} keys at depth #{depth}"
when Array
"Array with #{data.size} items at depth #{depth}"
else
"#{data.class} at depth #{depth}"
end
end
def self.calculate_depth(data, current_depth = 0)
case data
when Hash
data.values.map { |v| calculate_depth(v, current_depth + 1) }.max || current_depth
when Array
data.map { |v| calculate_depth(v, current_depth + 1) }.max || current_depth
else
current_depth
end
end
end
module JSONDebugger
def self.analyze_json(json_string)
begin
# JSONの構造解析
data = JSON.parse(json_string)
{
valid: true,
structure: analyze_structure(data),
size: json_string.bytesize,
depth: calculate_depth(data)
}
rescue JSON::ParserError => e
{
valid: false,
error: e.message,
position: e.pos,
snippet: json_string[([e.pos - 20, 0].max)..(e.pos + 20)]
}
end
end
private
def self.analyze_structure(data, depth = 0)
case data
when Hash
"Hash with #{data.keys.size} keys at depth #{depth}"
when Array
"Array with #{data.size} items at depth #{depth}"
else
"#{data.class} at depth #{depth}"
end
end
def self.calculate_depth(data, current_depth = 0)
case data
when Hash
data.values.map { |v| calculate_depth(v, current_depth + 1) }.max || current_depth
when Array
data.map { |v| calculate_depth(v, current_depth + 1) }.max || current_depth
else
current_depth
end
end
end
これらのテクニックを組み合わせることで、より堅牢なJSONデータ処理を実現できます。
パフォーマンス最適化のテクニック
大規模なJSONデータ処理の効率化方法
大規模なJSONデータを効率的に処理するための様々なテクニックを紹介します。
ストリーミング処理の活用
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
require 'oj'# 高速なJSONパーサー
require 'benchmark'
# ストリーミングパーサーの実装
class StreamParser < Oj::Saj
def initialize
@results = []
end
def hash_start
# ハッシュの開始時の処理
end
def hash_end
# ハッシュの終了時の処理
end
def array_start
# 配列の開始時の処理
end
def array_end
# 配列の終了時の処理
end
defadd_value(value)
@results << value if value.is_a?(Hash)
end
attr_reader :results
end
# 使用例
parser = StreamParser.new
Oj.load_file('large_data.json', handler: parser)
require 'oj' # 高速なJSONパーサー
require 'benchmark'
# ストリーミングパーサーの実装
class StreamParser < Oj::Saj
def initialize
@results = []
end
def hash_start
# ハッシュの開始時の処理
end
def hash_end
# ハッシュの終了時の処理
end
def array_start
# 配列の開始時の処理
end
def array_end
# 配列の終了時の処理
end
def add_value(value)
@results << value if value.is_a?(Hash)
end
attr_reader :results
end
# 使用例
parser = StreamParser.new
Oj.load_file('large_data.json', handler: parser)
require 'oj' # 高速なJSONパーサー
require 'benchmark'
# ストリーミングパーサーの実装
class StreamParser < Oj::Saj
def initialize
@results = []
end
def hash_start
# ハッシュの開始時の処理
end
def hash_end
# ハッシュの終了時の処理
end
def array_start
# 配列の開始時の処理
end
def array_end
# 配列の終了時の処理
end
def add_value(value)
@results << value if value.is_a?(Hash)
end
attr_reader :results
end
# 使用例
parser = StreamParser.new
Oj.load_file('large_data.json', handler: parser)
バッチ処理の実装
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class BatchProcessor
definitialize(batch_size = 1000)
@batch_size = batch_size
end
defprocess_large_json(file_path)
File.open(file_path)do |file|
parser = Oj::Parser.new(:compat)
batch = []
parser.parse_file(file)do |record|
batch << record
if batch.size>= @batch_size
process_batch(batch)
batch.clear
end
end
process_batch(batch) unless batch.empty?
end
end
private
defprocess_batch(batch)
# バッチ処理の実装
batch.eachdo |record|
# レコードの処理
end
end
end
class BatchProcessor
def initialize(batch_size = 1000)
@batch_size = batch_size
end
def process_large_json(file_path)
File.open(file_path) do |file|
parser = Oj::Parser.new(:compat)
batch = []
parser.parse_file(file) do |record|
batch << record
if batch.size >= @batch_size
process_batch(batch)
batch.clear
end
end
process_batch(batch) unless batch.empty?
end
end
private
def process_batch(batch)
# バッチ処理の実装
batch.each do |record|
# レコードの処理
end
end
end
class BatchProcessor
def initialize(batch_size = 1000)
@batch_size = batch_size
end
def process_large_json(file_path)
File.open(file_path) do |file|
parser = Oj::Parser.new(:compat)
batch = []
parser.parse_file(file) do |record|
batch << record
if batch.size >= @batch_size
process_batch(batch)
batch.clear
end
end
process_batch(batch) unless batch.empty?
end
end
private
def process_batch(batch)
# バッチ処理の実装
batch.each do |record|
# レコードの処理
end
end
end
メモリ使用量を考慮したストリーミング処理
メモリ使用量を最小限に抑えながら大規模なJSONデータを処理する方法を解説します。
メモリ効率の良い実装
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
require 'json/stream'
class MemoryEfficientParser
defparse_large_file(file_path)
parser = JSON::Stream::Parser.newdo |records|
records.array_startdo
# 配列の開始時の処理
end
records.array_enddo
# 配列の終了時の処理
end
records.object_startdo
# オブジェクトの開始時の処理
end
records.object_enddo
# オブジェクトの終了時の処理
end
records.valuedo |value|
process_value(value)
end
end
File.open(file_path)do |file|
while chunk = file.read(8192)
parser << chunk
end
end
end
private
defprocess_value(value)
# 値の処理
end
end
require 'json/stream'
class MemoryEfficientParser
def parse_large_file(file_path)
parser = JSON::Stream::Parser.new do |records|
records.array_start do
# 配列の開始時の処理
end
records.array_end do
# 配列の終了時の処理
end
records.object_start do
# オブジェクトの開始時の処理
end
records.object_end do
# オブジェクトの終了時の処理
end
records.value do |value|
process_value(value)
end
end
File.open(file_path) do |file|
while chunk = file.read(8192)
parser << chunk
end
end
end
private
def process_value(value)
# 値の処理
end
end
require 'json/stream'
class MemoryEfficientParser
def parse_large_file(file_path)
parser = JSON::Stream::Parser.new do |records|
records.array_start do
# 配列の開始時の処理
end
records.array_end do
# 配列の終了時の処理
end
records.object_start do
# オブジェクトの開始時の処理
end
records.object_end do
# オブジェクトの終了時の処理
end
records.value do |value|
process_value(value)
end
end
File.open(file_path) do |file|
while chunk = file.read(8192)
parser << chunk
end
end
end
private
def process_value(value)
# 値の処理
end
end
パフォーマンス測定とモニタリング
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
require 'memory_profiler'
class PerformanceMonitor
def self.measure_memory_usage
MemoryProfiler.reportdo
yield
end
end
def self.measure_execution_time
start_time = Time.now
result = yield
end_time = Time.now
{
result: result,
execution_time: end_time - start_time
}
end
end
# 使用例
PerformanceMonitor.measure_memory_usagedo
# メモリ使用量を測定したい処理
large_json = File.read('large_data.json')
JSON.parse(large_json)
end
result = PerformanceMonitor.measure_execution_timedo
# 実行時間を測定したい処理
process_json_data(large_json)
end
require 'memory_profiler'
class PerformanceMonitor
def self.measure_memory_usage
MemoryProfiler.report do
yield
end
end
def self.measure_execution_time
start_time = Time.now
result = yield
end_time = Time.now
{
result: result,
execution_time: end_time - start_time
}
end
end
# 使用例
PerformanceMonitor.measure_memory_usage do
# メモリ使用量を測定したい処理
large_json = File.read('large_data.json')
JSON.parse(large_json)
end
result = PerformanceMonitor.measure_execution_time do
# 実行時間を測定したい処理
process_json_data(large_json)
end
require 'memory_profiler'
class PerformanceMonitor
def self.measure_memory_usage
MemoryProfiler.report do
yield
end
end
def self.measure_execution_time
start_time = Time.now
result = yield
end_time = Time.now
{
result: result,
execution_time: end_time - start_time
}
end
end
# 使用例
PerformanceMonitor.measure_memory_usage do
# メモリ使用量を測定したい処理
large_json = File.read('large_data.json')
JSON.parse(large_json)
end
result = PerformanceMonitor.measure_execution_time do
# 実行時間を測定したい処理
process_json_data(large_json)
end
キャッシュの活用
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
require 'lru_redux'
class JSONCache
definitialize(max_size = 1000)
@cache = LruRedux::Cache.new(max_size)
end
deffetch(key)
@cache.fetch(key)do
yield
end
end
def clear
@cache.clear
end
end
# 使用例
json_cache = JSONCache.new
result = json_cache.fetch('key')do
JSON.parse(large_json_string)
end
require 'lru_redux'
class JSONCache
def initialize(max_size = 1000)
@cache = LruRedux::Cache.new(max_size)
end
def fetch(key)
@cache.fetch(key) do
yield
end
end
def clear
@cache.clear
end
end
# 使用例
json_cache = JSONCache.new
result = json_cache.fetch('key') do
JSON.parse(large_json_string)
end
require 'lru_redux'
class JSONCache
def initialize(max_size = 1000)
@cache = LruRedux::Cache.new(max_size)
end
def fetch(key)
@cache.fetch(key) do
yield
end
end
def clear
@cache.clear
end
end
# 使用例
json_cache = JSONCache.new
result = json_cache.fetch('key') do
JSON.parse(large_json_string)
end
module JSONValidator
class ValidationError < StandardError; end
def self.validate_input(json_data, schema)
# スキーマに基づく検証
validate_structure(json_data, schema)
validate_data_types(json_data, schema)
validate_value_ranges(json_data, schema)
true
rescue ValidationError => e
Rails.logger.error("バリデーションエラー: #{e.message}")
false
end
private
def self.validate_structure(data, schema)
schema.each do |key, rules|
unless data.key?(key)
raise ValidationError, "必須キーが存在しません: #{key}"
end
end
end
def self.validate_data_types(data, schema)
schema.each do |key, rules|
expected_type = rules[:type]
actual_value = data[key]
unless actual_value.is_a?(expected_type)
raise ValidationError, "不正なデータ型: #{key}"
end
end
end
def self.validate_value_ranges(data, schema)
schema.each do |key, rules|
next unless rules[:range]
value = data[key]
range = rules[:range]
unless range.include?(value)
raise ValidationError, "値が範囲外です: #{key}"
end
end
end
end
module JSONValidator
class ValidationError < StandardError; end
def self.validate_input(json_data, schema)
# スキーマに基づく検証
validate_structure(json_data, schema)
validate_data_types(json_data, schema)
validate_value_ranges(json_data, schema)
true
rescue ValidationError => e
Rails.logger.error("バリデーションエラー: #{e.message}")
false
end
private
def self.validate_structure(data, schema)
schema.each do |key, rules|
unless data.key?(key)
raise ValidationError, "必須キーが存在しません: #{key}"
end
end
end
def self.validate_data_types(data, schema)
schema.each do |key, rules|
expected_type = rules[:type]
actual_value = data[key]
unless actual_value.is_a?(expected_type)
raise ValidationError, "不正なデータ型: #{key}"
end
end
end
def self.validate_value_ranges(data, schema)
schema.each do |key, rules|
next unless rules[:range]
value = data[key]
range = rules[:range]
unless range.include?(value)
raise ValidationError, "値が範囲外です: #{key}"
end
end
end
end
安全なJSONパース処理の実装方法
セキュアなJSONパース処理を実装するためのベストプラクティスを紹介します。
セキュアなパーサーの実装
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class SecureJSONParser
MAX_STRING_LENGTH = 100_000
MAX_ARRAY_LENGTH = 1_000
MAX_NESTING_LEVEL = 20
def self.parse(json_string, options = {})
# 文字列長のチェック
raise 'Input too long'if json_string.length> MAX_STRING_LENGTH