Re: Creating Ruby Classes from XSD?



On Fri, Feb 24, 2006 at 10:27:52PM +0900, Geoffrey Lane wrote:
[...]
} XSD schema (for all of its imperfections) has the ability to specify
} constraints on the data that help validate it. Up front your service
} can define things like:
}
} (pseudo-XSD)
} US Address:
} Street1 = required
} Street2 = optional
} City = required
} State = [A-Z]{2}
} USZip = [0-9]{5}(-[0-9]{4})?
}
} Those kinds of constraints can be done in code, but no language I
} know of can make an interface to a method that explicit.

Converting those constraints to Ruby is a challenge, but far from
impossible. First off, I'd store the fields in a hash and override
method_missing to implement accessors and mutators as requested (the same
way ActiveRecord implements find_by_*).

The regex constraints are relatively simple. They just require
minor adjustments to account for the differences in regex flavors between
XSD and Ruby. I'd store a hash of symbol to regex-and-optional for use by a
generalized setter method.

The required/optional issue is dealt with in the constructor. The
constructor takes a (possibly nested) hash, raises an exception if a
required field is missing or an unknown field is present, and uses mutators
to set the fields. You might also have a constructor that converts an XML
(sub-)tree into a (nested) hash before passing it to the other constructor.
Implementing your example:

class XSDclass

def set_field(symbol, value)
constraint = constraints[symbol]
fail "No such field" unless constraint
valregex = constraint[:valregex]
if valregex && valregex !~ value
fail "Invalid value '#{value}' for field #{symbol}"
end
@fields[symbol] = value
end

def method_missing(method_id, *arguments)
if constraints[method_id] && arguments.length == 0
return @fields[method_id]
elsif method_id.to_s[-1].chr == '='
setter_for = method_id.to_s[0...-1].to_sym
if constraints[setter_for] && arguments.length == 1
return set_field(setter_for, arguments[0])
end
end
super
end

private

def constraints
{}
end

def initialize(hashed_fields)
required_fields = constraints.select { |k,v| not v[:optional] }
required_fields.map! { |f| f[0] }
missing = (required_fields - hashed_fields.keys).join(', ')
excess = (hashed_fields.keys - constraints.keys).join(', ')
if (missing.length + excess.length) != 0
errors = "Validation error constructing #{self.class}:\n\n"
errors << "\tMissing fields: #{missing}\n" if missing.length > 0
errors << "\tUnknown fields: #{excess}\n" if excess.length > 0
fail errors
end
@fields = {}
hashed_fields.each { |k,v| set_field(k,v) }
end

end

class US_Address < XSDclass

Constraints = { :street1 => { :optional => false },
:street2 => { :optional => true },
:city => { :optional => false },
:state => { :valregex => /[A-Z]{2}/,
:optional => false },
:uszip => { :valregex => /[0-9]{5}(-[0-9]{4})?/,
:optional => false }
}

def constraints
Constraints
end

def initialize(hashed_fields)
super
end

end

The conversion of XSD into a Ruby class definition, conversion of regular
expressions, a better implementation of the method_missing override (which
creates accessors/mutators for the next use), and the XML subtree
constructor are left as exercises to the reader. Note that I have actually
tested the code above; it works.

} Geoff Lane <geoff@xxxxxxxxxxx>
--Greg



.



Relevant Pages

  • Re: Brute force sudoku cracker
    ... def condense: ... raise ValueError() ... for _, (pos, square) in sqs: ...
    (comp.lang.python)
  • Re: Static variable vs Class variable
    ... Otherwise one would get interesting subtle differences with ... value satisfies some constraints ``x.a += b`` would trigger the set ... def setx: ... raise ValueError ...
    (comp.lang.python)
  • Re: XSD help
    ... You can embed Schematron rules in an XSD schema to specify co-occurrence ... constraints and layer over XSD validation. ...
    (microsoft.public.dotnet.xml)
  • Invalid Key node inside constraint error
    ... I have an XML document that I'm reading into a dataset. ... created dataadapters and constraints for all the tables and relationships and ... running the code to create the constraints all the time, ... WriteXMLSchema to write out an .xsd for me that has everything already ...
    (microsoft.public.dotnet.framework.adonet)
  • Re: XML Validation against XSD
    ... Is it possible using XSD? ... if you want to express such constraints then look into Schematron: ... Martin Honnen --- MVP XML ...
    (microsoft.public.dotnet.xml)