Data limite de entrega: 11 de Abril
Use como base do seu trabalho o projecto gradle disponível no repositório
jsonaif
.
Pretende-se desenvolver a biblioteca jsonaif
para processamento de dados em
formato JSON (https://www.json.org/).
Esta biblioteca disponibiliza um objecto JsonParserReflect
que pode ser usado
para transformar uma string JSON numa instância de uma classe de domínio
compatível (e.g. Student
) conforme ilustrado no exemplo seguinte:
val json = "{ name: \"Ze Manel\", nr: 7353}"
val student = JsonParserReflect.parse(json, Student::class) as Student
assertEquals("Ze Manel", student.name)
assertEquals(7353, student.nr)
A class JsonParserReflect
usa uma instância de uma classe auxiliar
JsonTokens
para percorrer os elementos da String JSON fonte.
O algoritmo de JsonParserReflect
é recursivo, criando instâncias de classes de
domínio, ou uma lista, e preenchendo os seus campos, ou elementos, com valores
primitivos ou instâncias de outras classes de domínio, ou listas, e assim
sucessivamente.
Ambas as classes são fornecidas no projecto jsonaif
.
Este enunciado tem 5 partes, sendo que as partes 1 e 2 têm um grau de dificuldade menor e uma dimensão mais reduzida do que cada uma das partes 3, 4 e 5.
A avaliação terá em conta não só a concretização com sucesso das 5 partes, mas também o progresso dos alunos ao longo da realização do trabalho, incluindo o que pode ser observado pelos commits no repositório git de suporte ao trabalho do grupo
Implemente os métodos parsePrimitive
e parseObject
de JsonParserReflect
de
modo a ter o comportamento desejado e satisfazer o testes unitário
parseSimpleObjectViaProperties()
disponibilizado no projecto jsonaif
.
Pode adicionar outros testes unitários além dos existentes.
Pode adicionar novos métodos auxiliares a JsonParseReflect
. A classe
JsonTokens
não deve ser modificada.
Implemente parseObject
de JsonParserReflect
instanciando a classe de domínio
através da chamada ao construtor sem parâmetros, ou que tem todos os
parâmetros opcionais (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect.full/create-instance.html).
Posteriormente afecta cada um das propriedades vindas da string JSON.
Note que nas classes de domínio dos testes do projecto jsonaif
apenas a
classes Student
pode ser instanciada nestas condições porque é a única que tem
um construtor com todos os parâmetros opcionais.
Refaça a implementação de JsonParserReflect
que mantenha uma estrutura de
dados com instâncias de Setter
para cada classe de domínio, de modo a que não
seja repetido o trabalho de leitura de metadata via Reflexão.
Por exemplo, no parsing de um array de Student
as propriedades a serem
afectadas só devem ser procuradas 1 vez.
A interface Setter
especifica a forma de afectação de uma determinada
propriedade no parâmetro target
a partir do valor obtido do parâmetro
tokens
:
interface Setter {
fun apply(target: Any, tokens: JsonTokens)
}
Por exemplo, na estrutura de dados seguinte cada classe de domínio é mapeada num
conjunto de pares: nome da propriedade - Setter
val setters = mutableMapOf<KClass<*>, Map<String, Setter>>()
Altere a classeJsonParserReflect
para suportar duas formas de instanciar a classe de domínio:
-
Através da chamada ao construtor sem parâmetros, ou que tem todos os parâmetros opcionais (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect.full/create-instance.html). E.g.
Student
-
Chamando um construtor com parâmetros. E.g.
Person
A implementação de parseObject
deve dar prioridade à opção 1, sempre que possível.
Implemente mais testes unitários incluindo por exemplo entidades de domínio como:
Classroom
que agrega um conjunto de instâncias deStudent
Account
com um saldo (balance) e um conjunto de movimentos de conta (transactions)- Um outro exemplo ao seu critério.
Pretende-se que as propriedades da classe de domínio possam ter nomes distintos
dos nomes usados na representação em JSON.
Por exemplo, uma propriedade em JSON pode ter o nome birth_date
e em Kotlin
birthDate
. Para resolver a correspondência entre propriedades de nome distinto
implemente uma anotação JsonProperty
que possa ser usada sobre propriedades de
uma classe de domínio indicando o nome correspondente em JSON (e.g.
@JsonProperty(“birth_date”)
).
Altere JsonParserReflect
para implementar o comportamento especificado e
valide com testes unitários.
Pretende-se ter uma forma alternativa de definir o valor de objectos sem ter que
seguir a sintaxe JSON.
Por exemplo, em vez de a propriedade birth
de Person
, do tipo
pt.isel.sample.Date
, ser definida em JSON, como no exemplo seguinte, poderá
ter uma forma alternativa como a que se apresenta para Student
:
- JSON for a Person:
"{ name: "Ze Manel", birth: { year: 1999, month: 9, day: 19}, sibling: { name: "Kata Badala"}}"
- JSON for a Student:
"{ name: "Maria Papoila", nr: 73753, birth: "1998-11-17" }"
Neste caso a propriedade birth
em Student
tem que ter uma anotação que
identifique a classe responsável por fazer a conversão de String
numa
instância de Date
.
Exemplo:
data class Student (var nr: Int = 0, var name: String? = null, @JsonConvert(JsonToDate::class) val birth: Date)
Implemente uma forma de poder associar através da anotação JsonConvert
um
conversor para qualquer classe de domínio.
JsonParserReflect
deve passar a ter em consideração esta anotação na
inicialização das instâncias de Setter
.
Além do exemplo dado, valide a sua implementação com outro exemplo de conversor para outro tipo de propriedade que deverá acrescentar a uma das classes de domínio.