Como implementar JsonConverter personalizado no JSON.NET para desserializar uma lista de objetos da classe base?
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
Como desserializar seguindo Json de volta à Lista <Pessoa>
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.
Veja também how-to-deserialize-json-into-ienumerablebasetype-com-json-net
Usando o padrão CustomCreationConverter, eu estava lutando para trabalhar como gerar o tipo correto ( Personou Employee), porque para determinar isso, você precisa analisar o JSON e não há uma maneira integrada de fazer isso usando o Createmétodo
Encontrei 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 o ReadJsonmétodo e criando um novo Createmétodo abstrato que aceite a JObject.
A classe JObject fornece um meio de carregar um objeto JSON e fornece acesso aos dados nesse objeto.
O ReadJsonmétodo substituído cria um JObjecte chama o Createmétodo (implementado por nossa classe de conversor derivada), passando na JObjectinstância.
Essa JObjectinstância pode ser analisada para determinar o tipo correto, verificando a existência de determinados campos.
— jdavies
Seria bom ter o método WriteJson implementado também e fornecer algum método abstrato para a especificação do tipo.
NOTA: Esta solução está em toda a Internet, mas possui uma falha que se manifesta em raras ocasiões. O novo JsonReadercriado no ReadJsonque 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 novo JsonReaderin serializer.Populate().
Para impedir a criação de um novo JsonReader (devido aos motivos mencionados por @Alain) ou se você precisar decidir sobre o tipo de objeto criado com base em algum valor do pai, consulte esta solução stackoverflow.com/a/22539730/1038496 . Parece mais eficaz e mais claro para mim (mesmo para esse tipo de problema).
— Zoka 21/03/14
@Triynko: Depois de muito tempo pesquisando, descobri que a classe JsonConvertertem uma propriedade chamada CanReade CanWrite. Se você não precisa de uma WriteJsonimplementação personalizada , basta permitir o CanWriteretorno FALSE. O sistema retornará ao comportamento padrão. @jdavies: adicione isso à sua resposta. Caso contrário, ele falhará na serialização.
Eu descobri que você deve lidar com casos NULL, caso contrário, um erro agradável ocorrerá. Uso: ||| if (reader.TokenType == JsonToken.Null) retorna nulo; |||| Fonte: stackoverflow.com/a/34185296/857291
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:
Não se esqueça de pensar no CanWrite! (Eu defino como false) Você pode terminar com loops de auto-referência (eu fiz). stackoverflow.com/questions/12314438/…
— Dribbel
Você também não precisa implementar o WriteJson? Como o conversor sabe converter do objeto para json?
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:
Eu realmente gosto dessa solução, mas vejo um problema quando existem vários tipos conhecidos que têm os mesmos nomes de propriedade exatos. Você encontrou esse problema? THX.
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:
— manuc66
Conversor muito útil. Apenas me economizei horas codificando um conversor!
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.
— zlangner
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
— Denis Pitcher
Aqui está outra solução que evita o uso de jObject.CreateReader(), e cria um novo JsonTextReader(que é o comportamento usado pelo JsonCreate.Deserialzemétodo padrão :
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:
— Robert Michael Watson
Usando a idéia de totem e zlangner , criei umKnownTypeConverter 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, Documentfoi criada uma classe base que inclui um conjunto comum de propriedades. Também são criadas duas classes herdadas: - OutgoingDocumentadiciona dois elementos opcionais "device_id"e "msg_id"; - IncomingDocumentadiciona 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.
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 da OutgoingDocumentclasse será criado, uma vez que é listado primeiro na lista de KnownTypeAttributeatributos. A seu pedido, você pode variar a implementação do KnownTypeConverternessa situação.