Estou tentando estender o exemplo JSON.net fornecido aqui http://james.newtonking.com/projects/json/help/CustomCreationConverter.html
Eu tenho outra subclasse derivada da classe base / Interface
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
List<Person> people = new List<Person>
{
new Employee(),
new Employee(),
new Artist(),
};
Como desserializar seguindo Json de volta à Lista <Pessoa>
[
{
"Department": "Department1",
"JobTitle": "JobTitle1",
"FirstName": "FirstName1",
"LastName": "LastName1"
},
{
"Department": "Department2",
"JobTitle": "JobTitle2",
"FirstName": "FirstName2",
"LastName": "LastName2"
},
{
"Skill": "Painter",
"FirstName": "FirstName3",
"LastName": "LastName3"
}
]
Não quero usar TypeNameHandling JsonSerializerSettings. Estou procurando especificamente a implementação JsonConverter personalizada para lidar com isso. A documentação e exemplos em torno disso são muito escassos na rede. Não consigo entender direito a implementação do método ReadJson () substituído no JsonConverter.
c#
json
json.net
deserialization
Snakebyte
fonte
fonte
Respostas:
Usando o padrão
CustomCreationConverter
, eu estava lutando para trabalhar como gerar o tipo correto (Person
ouEmployee
), porque para determinar isso, você precisa analisar o JSON e não há uma maneira integrada de fazer isso usando oCreate
métodoEncontrei um tópico de discussão referente à conversão de tipo e ele acabou fornecendo a resposta. Aqui está um link: Tipo de conversão .
O que é necessário é subclasse
JsonConverter
, substituindo oReadJson
método e criando um novoCreate
método abstrato que aceite aJObject
.O
ReadJson
método substituído cria umJObject
e chama oCreate
método (implementado por nossa classe de conversor derivada), passando naJObject
instância.Essa
JObject
instância pode ser analisada para determinar o tipo correto, verificando a existência de determinados campos.Exemplo
fonte
JsonReader
criado noReadJson
que método não herdou nenhum dos valores de configuração do leitor de origem (Culture
,DateParseHandling
,DateTimeZoneHandling
,FloatParseHandling
, etc ...). Esses valores devem ser copiados antes de usar o novoJsonReader
inserializer.Populate()
.JsonConverter
tem uma propriedade chamadaCanRead
eCanWrite
. Se você não precisa de umaWriteJson
implementação personalizada , basta permitir oCanWrite
retornoFALSE
. O sistema retornará ao comportamento padrão. @jdavies: adicione isso à sua resposta. Caso contrário, ele falhará na serialização.A solução acima
JsonCreationConverter<T>
está disponível em toda a Internet, mas possui uma falha que se manifesta em raras ocasiões. O novo JsonReader criado no método ReadJson não herda nenhum dos valores de configuração do leitor original (Culture, DateParseHandling, DateTimeZoneHandling, FloatParseHandling, etc ...). Esses valores devem ser copiados antes de usar o novo JsonReader no serializer.Populate ().Este é o melhor que pude resolver para corrigir alguns dos problemas da implementação acima, mas ainda acho que há algumas coisas que estão sendo esquecidas:
Atualização Atualizei isso para ter um método mais explícito que faça uma cópia de um leitor existente. Isso apenas encapsula o processo de cópia nas configurações individuais do JsonReader. Idealmente, essa função seria mantida na própria biblioteca Newtonsoft, mas por enquanto, você pode usar o seguinte:
Isso deve ser usado da seguinte maneira:
A solução mais antiga é a seguir:
fonte
Apenas pensei em compartilhar uma solução também baseada nisso que funciona com o atributo Knowntype usando reflexão, tinha que obter classe derivada de qualquer classe base, a solução pode se beneficiar da recursão para encontrar a melhor classe correspondente, embora eu não precisasse dela no meu Nesse caso, a correspondência é feita pelo tipo dado ao conversor; se ele tiver KnownTypes, ele verificará todos eles até que corresponda a um tipo que possua todas as propriedades dentro da string json, a primeira a corresponder será escolhida.
o uso é tão simples quanto:
no caso acima, ret será do tipo B.
Classes JSON:
Código do conversor:
fonte
O projeto JsonSubTypes implementa um conversor genérico que manipula esse recurso com a ajuda de atributos.
Para a amostra de concreto fornecida aqui, é assim que funciona:
fonte
Esta é uma expansão para a resposta do totem. Ele basicamente faz a mesma coisa, mas a correspondência de propriedades é baseada no objeto json serializado, não reflete o objeto .net. Isso é importante se você estiver usando [JsonProperty], usando o CamelCasePropertyNamesContractResolver ou fazendo qualquer outra coisa que faça com que o json não corresponda ao objeto .net.
O uso é simples:
Código do conversor:
fonte
Como outra variação na solução de tipos conhecidos do Totem, você pode usar a reflexão para criar um resolvedor de tipos genéricos para evitar a necessidade de usar atributos de tipos conhecidos.
Isso usa uma técnica semelhante ao GenericResolver do Juval Lowy para WCF.
Enquanto sua classe base for abstrata ou uma interface, os tipos conhecidos serão determinados automaticamente em vez de serem decorados com atributos de tipo conhecidos.
No meu próprio caso, optei por usar uma propriedade $ type para designar tipo no meu objeto json, em vez de tentar determiná-la a partir das propriedades, embora você possa emprestar outras soluções aqui para usar a determinação baseada em propriedade.
Em seguida, ele pode ser instalado como um formatador
fonte
Aqui está outra solução que evita o uso de
jObject.CreateReader()
, e cria um novoJsonTextReader
(que é o comportamento usado peloJsonCreate.Deserialze
método padrão :fonte
Muitas vezes a implementação existirá no mesmo espaço para nome da interface. Então, eu vim com isso:
Portanto, você pode incluir isso globalmente da seguinte maneira:
fonte
Usando a idéia de totem e zlangner , criei um
KnownTypeConverter
que será capaz de determinar o herdeiro mais apropriado, levando em consideração que os dados json podem não ter elementos opcionais.Portanto, o serviço envia uma resposta JSON que contém uma matriz de documentos (entrada e saída). Os documentos têm um conjunto comum de elementos e diferentes. Nesse caso, os elementos relacionados aos documentos de saída são opcionais e podem estar ausentes.
Nesse sentido,
Document
foi criada uma classe base que inclui um conjunto comum de propriedades. Também são criadas duas classes herdadas: -OutgoingDocument
adiciona dois elementos opcionais"device_id"
e"msg_id"
; -IncomingDocument
adiciona um elemento obrigatório"sender_id"
;A tarefa era criar um conversor que, com base nos dados e nas informações json do KnownTypeAttribute, pudesse determinar a classe mais apropriada que permite salvar a maior quantidade de informações recebidas. Também deve ser levado em consideração que os dados json podem não ter elementos opcionais. Para reduzir o número de comparações de elementos json e propriedades dos modelos de dados, decidi não levar em consideração as propriedades da classe base e correlacionar com os elementos json apenas as propriedades das classes herdadas.
Dados do serviço:
Modelos de dados:
Conversor:
PS: No meu caso, se nenhum herdeiro não tiver sido selecionado pelo conversor (isso pode acontecer se os dados JSON contiverem informações apenas da classe base ou os dados JSON não contiverem elementos opcionais da
OutgoingDocument
), um objeto daOutgoingDocument
classe será criado, uma vez que é listado primeiro na lista deKnownTypeAttribute
atributos. A seu pedido, você pode variar a implementação doKnownTypeConverter
nessa situação.fonte