User customizable heuristic to infer the name of the Nit class to deserialize json_object

This method is called only when deserializing an object without the metadata __class. Use the content of json_object to identify what Nit class it should be deserialized into. Or use self.attributes_path indicating where the deserialized object will be stored, is is less reliable as some objects don't have an associated attribute: the root/first deserialized object and collection elements.

Return the class name as a String when it can be inferred, or null when the class name cannot be found.

If a valid class name is returned, json_object will then be deserialized normally. So it must contain the attributes of the corresponding class, as usual.

class MyData
    serialize

    var data: String
end

class MyError
    serialize

    var error: String
    var related_data: MyData
end

class MyJsonDeserializer
    super JsonDeserializer

    redef fun class_name_heuristic(json_object)
    do
        # Infer the Nit class from the content of the JSON object.
        if json_object.keys.has("error") then return "MyError"
        if json_object.keys.has("data") then return "MyData"

        # Infer the Nit class from the attribute where it will be stored.
        # This line duplicates a previous line, and would only apply when
        # `MyData` is within a `MyError`.
        if attributes_path.not_empty and attributes_path.last == "related_data" then return "MyData"

        return null
    end
end

var json = """{"data": "some data"}"""
var deserializer = new MyJsonDeserializer(json)
var deserialized = deserializer.deserialize
assert deserializer.errors.is_empty
assert deserialized isa MyData

json = """{"error": "some error message",
           "related_data": {"data": "some other data"}}"""
deserializer = new MyJsonDeserializer(json)
deserialized = deserializer.deserialize
assert deserializer.errors.is_empty
assert deserialized isa MyError

Property definitions

json $ JsonDeserializer :: class_name_heuristic
	# User customizable heuristic to infer the name of the Nit class to deserialize `json_object`
	#
	# This method is called only when deserializing an object without the metadata `__class`.
	# Use the content of `json_object` to identify what Nit class it should be deserialized into.
	# Or use `self.attributes_path` indicating where the deserialized object will be stored,
	# is is less reliable as some objects don't have an associated attribute:
	# the root/first deserialized object and collection elements.
	#
	# Return the class name as a `String` when it can be inferred,
	# or `null` when the class name cannot be found.
	#
	# If a valid class name is returned, `json_object` will then be deserialized normally.
	# So it must contain the attributes of the corresponding class, as usual.
	#
	# ~~~
	# class MyData
	#     serialize
	#
	#     var data: String
	# end
	#
	# class MyError
	#     serialize
	#
	#     var error: String
	#     var related_data: MyData
	# end
	#
	# class MyJsonDeserializer
	#     super JsonDeserializer
	#
	#     redef fun class_name_heuristic(json_object)
	#     do
	#         # Infer the Nit class from the content of the JSON object.
	#         if json_object.keys.has("error") then return "MyError"
	#         if json_object.keys.has("data") then return "MyData"
	#
	#         # Infer the Nit class from the attribute where it will be stored.
	#         # This line duplicates a previous line, and would only apply when
	#         # `MyData` is within a `MyError`.
	#         if attributes_path.not_empty and attributes_path.last == "related_data" then return "MyData"
	#
	#         return null
	#     end
	# end
	#
	# var json = """{"data": "some data"}"""
	# var deserializer = new MyJsonDeserializer(json)
	# var deserialized = deserializer.deserialize
	# assert deserializer.errors.is_empty
	# assert deserialized isa MyData
	#
	# json = """{"error": "some error message",
	#            "related_data": {"data": "some other data"}}"""
	# deserializer = new MyJsonDeserializer(json)
	# deserialized = deserializer.deserialize
	# assert deserializer.errors.is_empty
	# assert deserialized isa MyError
	# ~~~
	protected fun class_name_heuristic(json_object: Map[String, nullable Object]): nullable String
	do
		return null
	end
lib/json/serialization_read.nit:298,2--360,4

github $ GithubDeserializer :: class_name_heuristic
	redef fun class_name_heuristic(raw) do
		# Try with url
		var class_name = url_heuristic(raw)
		if class_name != null then return class_name

		# print raw.serialize_to_json(true, true) # debug

		# Use properties heuristics
		if raw.has_key("name") and raw.has_key("commit") then
			return "Branch"
		else if raw.has_key("total_count") and raw.has_key("items") then
			return "SearchResults"
		else if raw.has_key("total") and raw.has_key("weeks") then
			return "ContributorStats"
		else if raw.has_key("a") and raw.has_key("d") and raw.has_key("c") then
			return "ContributorWeek"
		end
		return null
	end
lib/github/api.nit:1111,2--1129,4

github :: events $ GithubDeserializer :: class_name_heuristic
	redef fun class_name_heuristic(json_object) do
		if json_object.has_key("action") and json_object.has_key("commit") and json_object.has_key("comment") then
			return "CommitCommentEvent"
		else if json_object.has_key("ref") and json_object.has_key("master_branch") then
			return "CreateEvent"
		else if json_object.has_key("ref") and json_object.has_key("ref_type") then
			return "DeleteEvent"
		else if json_object.has_key("action") and json_object.has_key("sha") then
			return "DeploymentEvent"
		else if json_object.has_key("action") and json_object.has_key("state") then
			return "DeploymentStatusEvent"
		else if json_object.has_key("action") and json_object.has_key("forkee") then
			return "ForkEvent"
		else if json_object.has_key("action") and json_object.has_key("issue") and json_object.has_key("comment") then
			return "IssueCommentEvent"
		else if json_object.has_key("action") and json_object.has_key("issue") then
			return "IssuesEvent"
		else if json_object.has_key("action") and json_object.has_key("member") then
			return "MemberEvent"
		else if json_object.has_key("action") and json_object.has_key("number") then
			return "PullRequestEvent"
		else if json_object.has_key("action") and json_object.has_key("pull") and json_object.has_key("comment") then
			return "PullRequestPullCommentEvent"
		else if json_object.has_key("head_commit") and json_object.has_key("commits") then
			return "PushEvent"
		else if json_object.has_key("action") and json_object.has_key("branches") then
			return "StatusEvent"
		else if json_object.has_key("action") and json_object.has_key("issue") then
			return "GithubEvent"
		end
		return super
	end
lib/github/events.nit:249,2--280,4