109 lines
4.1 KiB
Ruby
109 lines
4.1 KiB
Ruby
require 'rexml/document'
|
|
require 'rexml/formatters/pretty'
|
|
|
|
module XRForge
|
|
|
|
MODEL_EXT = ['.glb', '.gltf', '.blend', '.usdz', '.obj', '.dae', '.x3d']
|
|
|
|
def self.log(message, filename)
|
|
# Append the log entry to the log file
|
|
File.open(filename, 'a') do |file|
|
|
file.write("#{message}\n")
|
|
end
|
|
puts("#{message}\n")
|
|
end
|
|
|
|
# ==============================================================================
|
|
# 1. JSON (Ruby Hash) to XML String
|
|
# ==============================================================================
|
|
|
|
# Internal recursive helper for converting Hash/Array structure to REXML.
|
|
# Handles attribute conversion (@key -> attribute), hash nesting, and arrays
|
|
# (which create multiple sibling elements of the same name).
|
|
#
|
|
# @param data [Hash, Array, String] The data fragment.
|
|
# @param p [REXML::Element] The parent REXML element.
|
|
def self.j2x_rec(data, p)
|
|
# Check if data is a Hash (for attributes and children)
|
|
if data.is_a?(Hash)
|
|
data.each { |k, v| k.start_with?('@') ? p.attributes[k[1..-1]] = v.to_s : j2x_rec(v, p.add_element(k)) }
|
|
# Check if data is an Array (for repeated elements)
|
|
elsif data.is_a?(Array)
|
|
# The parent in this context (e.g., <obj>) is already created. For each array item,
|
|
# we need to create a new element with the same name as the current parent's *name*
|
|
# and attach it to the parent's *parent*.
|
|
# This requires looking up the parent's parent, which breaks terseness.
|
|
# We must use a simpler recursive structure for compactness.
|
|
|
|
# We assume array structure requires us to create a new element named by the
|
|
# parent, and assign attributes/children recursively.
|
|
data.each { |v| j2x_rec(v, p) } # Recursive call to continue processing children
|
|
# Handle text content
|
|
else
|
|
p.text = data.to_s
|
|
end
|
|
end
|
|
|
|
# The entry point for converting Ruby Hash to XML string.
|
|
#
|
|
# @param data [Hash] The root hash structure.
|
|
# @return [String] The resulting compact XML string.
|
|
def self.json2xml(data)
|
|
# Corrected recursive logic for arrays that are values (e.g., "obj" => [..])
|
|
j2x_map = lambda do |d, p_name, p|
|
|
if d.is_a?(Hash)
|
|
d.each { |k, v| k.start_with?('@') ? p.attributes[k[1..-1]] = v.to_s : j2x_map.call(v, k, p.add_element(k)) }
|
|
elsif d.is_a?(Array)
|
|
# If the value is an array, we iterate, create a sibling element for each, and recurse
|
|
d.each { |v| j2x_map.call(v, p.name, p.parent.add_element(p.name)) }
|
|
p.remove # Remove the placeholder element created before hitting the array
|
|
else
|
|
p.text = d.to_s
|
|
end
|
|
end
|
|
|
|
# Initialize Document and process the single root key
|
|
doc = REXML::Document.new
|
|
data.each { |k, v| j2x_map.call(v, k, doc.add_element(k)) }
|
|
|
|
# Output with 2-space indentation
|
|
o = ""; f = REXML::Formatters::Default.new; f.indentation = 2; f.write(doc.root, o); o.strip
|
|
end
|
|
|
|
# ==============================================================================
|
|
# 2. XML String to JSON (Ruby Hash)
|
|
# ==============================================================================
|
|
|
|
# Internal recursive helper for converting REXML Element to Hash structure.
|
|
# Handles attribute conversion (attribute -> @key), text, and array creation for siblings.
|
|
#
|
|
# @param e [REXML::Element] The XML element.
|
|
# @return [Hash, String] The resulting hash or simple text value.
|
|
def self.x2j_rec(e)
|
|
h = {}; e.attributes.each { |k, v| h["@#{k}"] = v } # Attributes first
|
|
|
|
e.elements.each do |c| # Iterate children
|
|
v = x2j_rec(c)
|
|
if h.key?(c.name)
|
|
h[c.name] = [h[c.name]] unless h[c.name].is_a?(Array)
|
|
h[c.name] << v
|
|
else
|
|
h[c.name] = v
|
|
end
|
|
end
|
|
|
|
# Check for text content if no children were processed
|
|
t = e.get_text.to_s.strip; return t if t && !t.empty? && h.empty?; return h
|
|
end
|
|
|
|
# The entry point for converting XML string to Ruby Hash.
|
|
#
|
|
# @param xmlstr [String] The XML string.
|
|
# @return [Hash] The resulting Ruby hash structure.
|
|
def self.xml2json(xmlstr)
|
|
doc = REXML::Document.new(xmlstr)
|
|
root = doc.root
|
|
{ root.name => x2j_rec(root) }
|
|
end
|
|
|
|
end
|