Here is a very simple solution. It doesn't try to understand much of the contents of the .ged file, it just builds a tree of nodes and dumps them to xml. #!/usr/bin/env ruby require 'CGI' class String def indent(width) map {|line| ' ' * width + line.chomp}.join("\n") end end class Node def initialize(type, value) @type = type @value = CGI.escapeHTML(value) @children = [] end def <<(child) @children << child end def to_xml children = @children.map{|c| c.to_xml}.join("\n").indent(2) if @type[0] == ?@ return "<%s id=\"%s\">\n%s\n</%s>" % [@value.downcase, @type, children, @value.downcase] elsif children.empty? return "<%s>%s</%s>" % [@type.downcase, @value, @type.downcase] else if @value.empty? return "<%s>\n%s\n</%s>" % [@type.downcase, children, @type.downcase] else return "<%s value=\"%s\">\n%s\n</%s>" % [@type.downcase, @value, children, @type.downcase] end end end end if ARGV.size != 2 puts "Usage: gedparser.rb [input.ged] [ouput.xml]" exit end root = [] stack = [root] File.readlines(ARGV[0]).each do |line| line = line.strip depth, type, value = line.split(/\s+/, 3) next unless type value ||= '' node = Node.new(type, value) stack[depth.to_i] << node stack[depth.to_i + 1] = node end xml = root.map {|node| node.to_xml}.join("\n") xml = "<gedcom>\n" + xml.indent(2) + "\n</gedcom>" File.open(ARGV[1], 'w') {|f| f.puts xml}