Eu acho que ele provavelmente está interessado em mostrar o campo ID (ou similar) primeiro e depois todos os outros campos. este é mais amigável para os usuários finais do que olhar para ele depois de campos que começam com A..I
Michael Bahig
3
As propriedades JSON são definidas para serem desordenadas. Eu acho absolutamente bom forçar uma ordem de SAÍDA específica durante a serialização (talvez para dar uma olhada no JSON), mas seria uma má decisão criar uma DEPENDÊNCIA em qualquer ordem específica de desserialização.
DaBlick
5
Um par de razões válidas: (1) falsificando uma propriedade "$ type", que deve ser a primeira propriedade no JSON, (2) tentando gerar JSON que comprima o máximo possível
Stephen Chung
4
Outro motivo pode ser (3) uma representação canônica que usa a sintaxe JSON - o mesmo objeto deve ser garantido para produzir a mesma sequência JSON. Uma ordem determinística dos atributos é um pré-requisito necessário para isso.
MarkusSchaber
2
Kevin, você pode atualizar a resposta aceita nesta pergunta?
Millie Smith
Respostas:
256
A maneira suportada é usar o JsonPropertyatributo nas propriedades da classe para as quais você deseja definir a ordem. Leia a documentação do pedido JsonPropertyAttribute para obter mais informações.
Passe a JsonPropertyum Ordervalor e o serializador vai cuidar do resto.
[JsonProperty(Order=1)]
Isso é muito semelhante ao
DataMember(Order=1)
dos System.Runtime.Serializationdias.
Aqui está uma nota importante de @ kevin-babcock
... definir o pedido como 1 só funcionará se você definir um pedido maior que 1 em todas as outras propriedades. Por padrão, qualquer propriedade sem uma configuração de Pedido receberá uma ordem de -1. Portanto, você deve fornecer todas as propriedades serializadas e ordem ou definir seu primeiro item como -2
O uso da Orderpropriedade de JsonPropertyAttributepode ser usado para controlar a ordem na qual os campos são serializados / desserializados. No entanto, definir o pedido como 1 só funcionará se você definir um pedido maior que 1 em todas as outras propriedades. Por padrão, qualquer propriedade sem uma configuração de Pedido receberá uma ordem de -1. Portanto, você deve fornecer todas as propriedades e ordem serializadas ou definir seu primeiro item como -2.
Kevin Babcock
1
Funciona para serialização, mas o pedido não está sendo considerado na desserialização. De acordo com a documentação, o atributo order é usado para serialização e desserialização. Existe uma solução alternativa?
Cangosta
1
Existe uma propriedade semelhante para o arquivo JavaScriptSerializer.
Shimmy Weitzhandler
4
@cangosta A ordem de desserialização não deve importar .. exceto em alguns casos de expectativa muito "estranhos".
user2864740
1
Leia a discussão semelhante sobre questões do github em torno do desejo de que a Ordem seja respeitada na desserialização: github.com/JamesNK/Newtonsoft.Json/issues/758 Basicamente, não há chance disso.
Tyeth
126
Você pode realmente controlar a ordem através da implementação IContractResolverou substituindo o DefaultContractResolver's CreatePropertiesmétodo.
Aqui está um exemplo da minha implementação simples, IContractResolverque ordena as propriedades em ordem alfabética:
Isso é bastante útil (+1), mas uma ressalva: parece que a serialização de dicionários não usa essa personalização do CreateProperties. Eles serializam bem, mas não acabam classificados. Suponho que haja uma maneira diferente de personalizar a serialização de dicionários, mas não a encontrei.
Solublefish
Perfeito. Faz exatamente o que eu queria. Obrigado.
Wade Hatler,
Esta é uma otima soluçao. Funcionou perfeitamente para mim, especialmente ao colocar 2 objetos JSON lado a lado e ter as propriedades alinhadas.
Vince
16
No meu caso, a resposta de Mattias não funcionou. O CreatePropertiesmétodo nunca foi chamado.
Após alguma depuração de Newtonsoft.Jsoncomponentes internos, criei outra solução.
publicclassJsonUtility{publicstaticstringNormalizeJsonString(string json){// Parse json string into JObject.var parsedObject =JObject.Parse(json);// Sort properties of JObject.var normalizedObject =SortPropertiesAlphabetically(parsedObject);// Serialize JObject .returnJsonConvert.SerializeObject(normalizedObject);}privatestaticJObjectSortPropertiesAlphabetically(JObject original){var result =newJObject();foreach(var property in original.Properties().ToList().OrderBy(p => p.Name)){varvalue= property.ValueasJObject;if(value!=null){value=SortPropertiesAlphabetically(value);
result.Add(property.Name,value);}else{
result.Add(property.Name, property.Value);}}return result;}}
Esta foi a correção necessária para nós quando usamos dictos.
noocyte
Isso adiciona uma sobrecarga de desserialização e serialização adicionais. Eu adicionei uma solução which'll trabalho para aulas normais, dicionários e ExpandoObject (objeto dinâmico) bem
Jay Shah
11
No meu caso, a solução de niaher não funcionou porque não manipulava objetos em matrizes.
Isso adiciona uma sobrecarga de desserialização e serialização adicionais.
Jay Shah
Excelente solução. Obrigado.
Maia
3
Como Charlie observou, você pode controlar um pouco a ordem das propriedades JSON ordenando as propriedades na própria classe. Infelizmente, essa abordagem não funciona para propriedades herdadas de uma classe base. As propriedades da classe base serão ordenadas conforme dispostas no código, mas aparecerão antes das propriedades da classe base.
E para quem se pergunta por que você deseja alfabetizar as propriedades JSON, é muito mais fácil trabalhar com arquivos JSON brutos, principalmente para classes com muitas propriedades, se elas forem solicitadas.
Não era esse o comportamento de pedido padrão durante a serialização?
Mr5
1
Para economizar a alguém alguns minutos desperdiçados, observe que essa resposta não funciona para dicionários, apesar da reivindicação. CreatePropertiesnão é chamado durante a serialização de um dicionário. Eu explorei o repositório JSON.net para saber quais máquinas estão realmente arrastando as entradas do dicionário. Ele não se encaixa em nenhuma overrideou outra personalização para fazer pedidos. Ele apenas aceita as entradas do enumerador do objeto. Parece que tenho que construir um SortedDictionaryou SortedListforçar o JSON.net a fazer isso. Sugestão de recurso arquivada: github.com/JamesNK/Newtonsoft.Json/issues/2270
William
2
Se você não deseja colocar um JsonPropertyOrderatributo em todas as propriedades de classe, é muito simples criar seu próprio ContractResolver ...
A interface IContractResolver fornece uma maneira de personalizar como o JsonSerializer serializa e desserializa objetos .NET para JSON sem colocar atributos em suas classes.
Como isso:
privateclassSortedPropertiesContractResolver:DefaultContractResolver{// use a static instance for optimal performancestaticSortedPropertiesContractResolver instance;staticSortedPropertiesContractResolver(){ instance =newSortedPropertiesContractResolver();}publicstaticSortedPropertiesContractResolverInstance{get{return instance;}}protectedoverrideIList<JsonProperty>CreateProperties(Type type,MemberSerialization memberSerialization){var properties =base.CreateProperties(type, memberSerialization);if(properties !=null)return properties.OrderBy(p => p.UnderlyingName).ToList();return properties;}}
Implemento:
var settings =newJsonSerializerSettings{ContractResolver=SortedPropertiesContractResolver.Instance};var json =JsonConvert.SerializeObject(obj,Formatting.Indented, settings);
O método recursivo a seguir usa reflexão para classificar a lista de tokens internos em uma JObjectinstância existente, em vez de criar um novo gráfico de objeto classificado. Esse código depende de detalhes internos da implementação do Json.NET e não deve ser usado na produção.
voidSortProperties(JToken token){var obj = token asJObject;if(obj !=null){var props =typeof(JObject).GetField("_properties",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(obj);var items =typeof(Collection<JToken>).GetField("items",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(props);ArrayList.Adapter((IList) items).Sort(newComparisonComparer((x, y)=>{var xProp = x asJProperty;var yProp = y asJProperty;return xProp !=null&& yProp !=null?string.Compare(xProp.Name, yProp.Name):0;}));}foreach(var child in token.Children()){SortProperties(child);}}
Se você controlar (ou seja, escrever) a classe, coloque as propriedades em ordem alfabética e elas serão serializadas em ordem alfabética quando JsonConvert.SerializeObject()for chamada.
Eu quero serializar um objeto comblex e manter a ordem das propriedades conforme definidas no código. Não posso simplesmente adicionar [JsonProperty(Order = 1)]porque a própria classe está fora do meu escopo.
Essa solução também leva em consideração que as propriedades definidas em uma classe base devem ter uma prioridade mais alta.
Isso pode não ser à prova de balas, já que em nenhum lugar é definido que o MetaDataAttributegarante a ordem correta, mas parece funcionar. Para o meu caso de uso, tudo bem. desde que eu só quero manter a legibilidade humana para um arquivo de configuração gerado automaticamente.
publicclassPersonWithAge:Person{publicintAge{get;set;}}publicclassPerson{publicstringName{get;set;}}publicstringGetJson(){var thequeen =newPersonWithAge{Name="Elisabeth",Age=Int32.MaxValue};var settings =newJsonSerializerSettings(){ContractResolver=newMetadataTokenContractResolver(),};returnJsonConvert.SerializeObject(
thequeen,Newtonsoft.Json.Formatting.Indented, settings
);}publicclassMetadataTokenContractResolver:DefaultContractResolver{protectedoverrideIList<JsonProperty>CreateProperties(Type type,MemberSerialization memberSerialization){var props = type
.GetProperties(BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic).ToDictionary(k => k.Name, v =>{// first value: declaring typevar classIndex =0;var t = type;while(t != v.DeclaringType){
classIndex++;
t = type.BaseType;}returnTuple.Create(classIndex, v.MetadataToken);});returnbase.CreateProperties(type, memberSerialization).OrderByDescending(p => props[p.PropertyName].Item1).ThenBy(p => props[p.PropertyName].Item1).ToList();}}
Acabei de ver os votos negativos. Consulte a resposta de 'Steve' abaixo para saber como fazer isso.
ORIGINAL
Eu segui a JsonConvert.SerializeObject(key)chamada do método via reflexão (onde key era uma IList) e descobri que JsonSerializerInternalWriter.SerializeList é chamado. Leva uma lista e percorre através de
for (int i = 0; i < values.Count; i++) { ...
onde valores é o parâmetro IList trazido.
A resposta curta é ... Não, não existe uma maneira integrada de definir a ordem em que os campos são listados na string JSON.
@ Darin - mas há uma ordem na serialização. "{id: 1, name: 'John'}" e "{name: 'John', id: 1}" são diferentes como strings , e é com isso que eu me importo aqui. Obviamente, os objetos são equivalentes quando desserializados.
Kevin Montrose
1
@ Darin - não, não neste caso. Estou serializando algo e, em seguida, transmitindo-o como uma string para um serviço que lida apenas com strings (sem o conhecimento do JSON), e seria conveniente, por vários motivos, que um campo aparecesse primeiro na string.
Kevin Montrose
1
é bom para testar também, ser capaz de olhar apenas para as strings em vez de precisar desserializar.
26412 Steve Steve
9
Um pedido de serialização estável também é útil para validação de cache. É trivial obter uma soma de verificação de uma string - não é verdade para um gráfico de objeto completo.
Solublefish
1
A ordem de serialização também é útil ao fazer testes de unidade, para que você possa dizer com facilidade que as seqüências de resposta esperadas versus as reais são iguais, mesmo quando a ordem das propriedades json é diferente.
Respostas:
A maneira suportada é usar o
JsonProperty
atributo nas propriedades da classe para as quais você deseja definir a ordem. Leia a documentação do pedido JsonPropertyAttribute para obter mais informações.Passe a
JsonProperty
umOrder
valor e o serializador vai cuidar do resto.Isso é muito semelhante ao
dos
System.Runtime.Serialization
dias.Aqui está uma nota importante de @ kevin-babcock
fonte
Order
propriedade deJsonPropertyAttribute
pode ser usado para controlar a ordem na qual os campos são serializados / desserializados. No entanto, definir o pedido como 1 só funcionará se você definir um pedido maior que 1 em todas as outras propriedades. Por padrão, qualquer propriedade sem uma configuração de Pedido receberá uma ordem de -1. Portanto, você deve fornecer todas as propriedades e ordem serializadas ou definir seu primeiro item como -2.JavaScriptSerializer
.Você pode realmente controlar a ordem através da implementação
IContractResolver
ou substituindo oDefaultContractResolver
'sCreateProperties
método.Aqui está um exemplo da minha implementação simples,
IContractResolver
que ordena as propriedades em ordem alfabética:E defina as configurações e serialize o objeto, e os campos JSON estarão em ordem alfabética:
fonte
No meu caso, a resposta de Mattias não funcionou. O
CreateProperties
método nunca foi chamado.Após alguma depuração de
Newtonsoft.Json
componentes internos, criei outra solução.fonte
No meu caso, a solução de niaher não funcionou porque não manipulava objetos em matrizes.
Com base em sua solução, é isso que eu criei
fonte
Como Charlie observou, você pode controlar um pouco a ordem das propriedades JSON ordenando as propriedades na própria classe. Infelizmente, essa abordagem não funciona para propriedades herdadas de uma classe base. As propriedades da classe base serão ordenadas conforme dispostas no código, mas aparecerão antes das propriedades da classe base.
E para quem se pergunta por que você deseja alfabetizar as propriedades JSON, é muito mais fácil trabalhar com arquivos JSON brutos, principalmente para classes com muitas propriedades, se elas forem solicitadas.
fonte
Isso funcionará para classes normais, dicionários e ExpandoObject (objeto dinâmico) também.
fonte
CreateProperties
não é chamado durante a serialização de um dicionário. Eu explorei o repositório JSON.net para saber quais máquinas estão realmente arrastando as entradas do dicionário. Ele não se encaixa em nenhumaoverride
ou outra personalização para fazer pedidos. Ele apenas aceita as entradas do enumerador do objeto. Parece que tenho que construir umSortedDictionary
ouSortedList
forçar o JSON.net a fazer isso. Sugestão de recurso arquivada: github.com/JamesNK/Newtonsoft.Json/issues/2270Se você não deseja colocar um
JsonProperty
Order
atributo em todas as propriedades de classe, é muito simples criar seu próprio ContractResolver ...Como isso:
Implemento:
fonte
O método recursivo a seguir usa reflexão para classificar a lista de tokens internos em uma
JObject
instância existente, em vez de criar um novo gráfico de objeto classificado. Esse código depende de detalhes internos da implementação do Json.NET e não deve ser usado na produção.fonte
Na verdade, como meu Object já era um JObject, usei a seguinte solução:
e use-o assim:
fonte
Se você controlar (ou seja, escrever) a classe, coloque as propriedades em ordem alfabética e elas serão serializadas em ordem alfabética quando
JsonConvert.SerializeObject()
for chamada.fonte
Eu quero serializar um objeto comblex e manter a ordem das propriedades conforme definidas no código. Não posso simplesmente adicionar
[JsonProperty(Order = 1)]
porque a própria classe está fora do meu escopo.Essa solução também leva em consideração que as propriedades definidas em uma classe base devem ter uma prioridade mais alta.
Isso pode não ser à prova de balas, já que em nenhum lugar é definido que o
MetaDataAttribute
garante a ordem correta, mas parece funcionar. Para o meu caso de uso, tudo bem. desde que eu só quero manter a legibilidade humana para um arquivo de configuração gerado automaticamente.fonte
Se você deseja configurar globalmente sua API com campos ordenados, combine a resposta de Mattias Nordberg:
com a minha resposta aqui:
Como forçar a API da Web do ASP.NET a sempre retornar JSON?
fonte
ATUALIZAR
Acabei de ver os votos negativos. Consulte a resposta de 'Steve' abaixo para saber como fazer isso.
ORIGINAL
Eu segui a
JsonConvert.SerializeObject(key)
chamada do método via reflexão (onde key era uma IList) e descobri que JsonSerializerInternalWriter.SerializeList é chamado. Leva uma lista e percorre através defor (int i = 0; i < values.Count; i++) { ...
onde valores é o parâmetro IList trazido.
A resposta curta é ... Não, não existe uma maneira integrada de definir a ordem em que os campos são listados na string JSON.
fonte
Não há ordem de campos no formato JSON, portanto, definir uma ordem não faz sentido.
{ id: 1, name: 'John' }
é equivalente a{ name: 'John', id: 1 }
(ambos representam uma instância de objeto estritamente equivalente)fonte