答案 0 :(得分:211)
支持的方法是在要为其设置顺序的类属性上使用JsonProperty
属性。有关详细信息,请阅读JsonPropertyAttribute order documentation。
传递JsonProperty
Order
值,序列化工具将负责其余的工作。
[JsonProperty(Order = 1)]
这与
非常相似 DataMember(Order = 1)
<{1}}天的。
以下是来自@ kevin-babcock
的重要说明...将订单设置为1只有在所有其他属性上设置大于1的订单时才有效。默认情况下,任何没有Order设置的属性都将被赋予-1的顺序。因此,您必须提供所有序列化属性和订单,或将您的第一个项目设置为-2
答案 1 :(得分:114)
您实际上可以通过实施IContractResolver
或覆盖DefaultContractResolver
的{{1}}方法来控制订单。
以下是我简单实现CreateProperties
的示例,它按字母顺序对属性进行排序:
IContractResolver
然后设置设置并序列化对象,JSON字段将按字母顺序排列:
public class OrderedContractResolver : DefaultContractResolver
{
protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
{
return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
}
}
答案 2 :(得分:15)
就我而言,马蒂亚斯&#39;回答没有#39;去工作。从未调用CreateProperties
方法。
在对Newtonsoft.Json
内部进行一些调试之后,我提出了另一种解决方案。
public class JsonUtility
{
public static string NormalizeJsonString(string json)
{
// Parse json string into JObject.
var parsedObject = JObject.Parse(json);
// Sort properties of JObject.
var normalizedObject = SortPropertiesAlphabetically(parsedObject);
// Serialize JObject .
return JsonConvert.SerializeObject(normalizedObject);
}
private static JObject SortPropertiesAlphabetically(JObject original)
{
var result = new JObject();
foreach (var property in original.Properties().ToList().OrderBy(p => p.Name))
{
var value = property.Value as JObject;
if (value != null)
{
value = SortPropertiesAlphabetically(value);
result.Add(property.Name, value);
}
else
{
result.Add(property.Name, property.Value);
}
}
return result;
}
}
答案 3 :(得分:9)
在我的情况下,niaher的解决方案无效,因为它没有处理数组中的对象。
基于他的解决方案,这就是我想出来的
public static class JsonUtility
{
public static string NormalizeJsonString(string json)
{
JToken parsed = JToken.Parse(json);
JToken normalized = NormalizeToken(parsed);
return JsonConvert.SerializeObject(normalized);
}
private static JToken NormalizeToken(JToken token)
{
JObject o;
JArray array;
if ((o = token as JObject) != null)
{
List<JProperty> orderedProperties = new List<JProperty>(o.Properties());
orderedProperties.Sort(delegate(JProperty x, JProperty y) { return x.Name.CompareTo(y.Name); });
JObject normalized = new JObject();
foreach (JProperty property in orderedProperties)
{
normalized.Add(property.Name, NormalizeToken(property.Value));
}
return normalized;
}
else if ((array = token as JArray) != null)
{
for (int i = 0; i < array.Count; i++)
{
array[i] = NormalizeToken(array[i]);
}
return array;
}
else
{
return token;
}
}
}
答案 4 :(得分:3)
正如Charlie所说,您可以通过在类本身中对属性进行排序来控制JSON属性的排序。不幸的是,这种方法对从基类继承的属性不起作用。基类属性将按代码排列,但将出现在基类属性之前。
对于任何想知道为什么要按字母顺序排列JSON属性的人来说,使用原始JSON文件会更加容易,特别是对于具有大量属性的类,如果它们是有序的。
答案 5 :(得分:3)
如果您不想在每个类属性上放置一个<?xml version="1.0" encoding="utf-16"?>
<ShowPlanXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.1" Build="10.50.4000.0" xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple StatementCompId="1" StatementEstRows="1" StatementId="1" StatementOptmLevel="FULL" StatementOptmEarlyAbortReason="GoodEnoughPlanFound" StatementSubTreeCost="0.0179336" StatementText="SELECT COUNT(p.pKey) 
FROM Profile p LEFT OUTER JOIN 
 ( 
 SELECT hispKey, hisData FROM (
 SELECT hispKey, hisData, ROW_NUMBER() OVER (PARTITION BY hispKey ORDER BY hisIndex DESC) RowID 
 FROM HistoryData WHERE hisgKey = 318 AND hisType = 'SeveranceFinal' 
 ) t WHERE RowID = 1
 ) h ON p.pKey = h.hispKey
WHERE p.pgKey = 318
" StatementType="SELECT" QueryHash="0xFF065B887470862B" QueryPlanHash="0xC85BCB9BD79F6735">
<StatementSetOptions ANSI_NULLS="true" ANSI_PADDING="true" ANSI_WARNINGS="true" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="true" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="true" />
<QueryPlan CachedPlanSize="32" CompileTime="5" CompileCPU="5" CompileMemory="536">
<RelOp AvgRowSize="11" EstimateCPU="1.1E-06" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Compute Scalar" NodeId="0" Parallel="false" PhysicalOp="Compute Scalar" EstimatedTotalSubtreeCost="0.0179336">
<OutputList>
<ColumnReference Column="Expr1009" />
</OutputList>
<ComputeScalar>
<DefinedValues>
<DefinedValue>
<ColumnReference Column="Expr1009" />
<ScalarOperator ScalarString="CONVERT_IMPLICIT(int,[Expr1013],0)">
<Convert DataType="int" Style="0" Implicit="true">
<ScalarOperator>
<Identifier>
<ColumnReference Column="Expr1013" />
</Identifier>
</ScalarOperator>
</Convert>
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp AvgRowSize="11" EstimateCPU="1.1E-06" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Aggregate" NodeId="1" Parallel="false" PhysicalOp="Stream Aggregate" EstimatedTotalSubtreeCost="0.0179336">
<OutputList>
<ColumnReference Column="Expr1013" />
</OutputList>
<StreamAggregate>
<DefinedValues>
<DefinedValue>
<ColumnReference Column="Expr1013" />
<ScalarOperator ScalarString="Count(*)">
<Aggregate AggType="countstar" Distinct="false" />
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp AvgRowSize="9" EstimateCPU="4.18E-06" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Left Outer Join" NodeId="2" Parallel="false" PhysicalOp="Nested Loops" EstimatedTotalSubtreeCost="0.0179325">
<OutputList />
<NestedLoops Optimized="false">
<Predicate>
<ScalarOperator ScalarString="[RBL_Profile].[dbo].[Profile].[pKey] as [p].[pKey]=[RBL_Profile].[dbo].[HistoryData].[hispKey]">
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Alias="[p]" Column="pKey" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Identifier>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</Identifier>
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
<RelOp AvgRowSize="11" EstimateCPU="0.0001581" EstimateIO="0.003125" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Index Seek" NodeId="3" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0032831" TableCardinality="1371980">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Alias="[p]" Column="pKey" />
</OutputList>
<IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Alias="[p]" Column="pKey" />
</DefinedValue>
</DefinedValues>
<Object Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Index="[nc_Profile_GetProfiles]" Alias="[p]" IndexKind="NonClustered" />
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[Profile]" Alias="[p]" Column="pgKey" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="(318)">
<Const ConstValue="(318)" />
</ScalarOperator>
</RangeExpressions>
</Prefix>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
<RelOp AvgRowSize="11" EstimateCPU="4.8E-07" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Filter" NodeId="4" Parallel="false" PhysicalOp="Filter" EstimatedTotalSubtreeCost="0.014645">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</OutputList>
<Filter StartupExpression="false">
<RelOp AvgRowSize="19" EstimateCPU="8E-08" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Compute Scalar" NodeId="5" Parallel="false" PhysicalOp="Sequence Project" EstimatedTotalSubtreeCost="0.0146445">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
<ColumnReference Column="Expr1007" />
</OutputList>
<SequenceProject>
<DefinedValues>
<DefinedValue>
<ColumnReference Column="Expr1007" />
<ScalarOperator ScalarString="row_number">
<Sequence FunctionName="row_number" />
</ScalarOperator>
</DefinedValue>
</DefinedValues>
<RelOp AvgRowSize="19" EstimateCPU="2E-08" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Segment" NodeId="6" Parallel="false" PhysicalOp="Segment" EstimatedTotalSubtreeCost="0.0146444">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
<ColumnReference Column="Segment1012" />
</OutputList>
<Segment>
<GroupBy>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</GroupBy>
<SegmentColumn>
<ColumnReference Column="Segment1012" />
</SegmentColumn>
<RelOp AvgRowSize="22" EstimateCPU="0.000100022" EstimateIO="0.0112613" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Sort" NodeId="7" Parallel="false" PhysicalOp="Sort" EstimatedTotalSubtreeCost="0.0146444">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
</OutputList>
<MemoryFractions Input="1" Output="1" />
<Sort Distinct="false">
<OrderBy>
<OrderByColumn Ascending="true">
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</OrderByColumn>
<OrderByColumn Ascending="false">
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
</OrderByColumn>
</OrderBy>
<RelOp AvgRowSize="22" EstimateCPU="0.0001581" EstimateIO="0.003125" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Index Seek" NodeId="8" Parallel="false" PhysicalOp="Index Seek" EstimatedTotalSubtreeCost="0.0032831" TableCardinality="29897300">
<OutputList>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
</OutputList>
<IndexScan Ordered="true" ScanDirection="FORWARD" ForcedIndex="false" ForceSeek="false" ForceScan="false" NoExpandHint="false">
<DefinedValues>
<DefinedValue>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hispKey" />
</DefinedValue>
<DefinedValue>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisIndex" />
</DefinedValue>
</DefinedValues>
<Object Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Index="[IX_HistoryData_Group]" IndexKind="NonClustered" />
<SeekPredicates>
<SeekPredicateNew>
<SeekKeys>
<Prefix ScanType="EQ">
<RangeColumns>
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisType" />
<ColumnReference Database="[RBL_Profile]" Schema="[dbo]" Table="[HistoryData]" Column="hisgKey" />
</RangeColumns>
<RangeExpressions>
<ScalarOperator ScalarString="'SeveranceFinal'">
<Const ConstValue="'SeveranceFinal'" />
</ScalarOperator>
<ScalarOperator ScalarString="(318)">
<Const ConstValue="(318)" />
</ScalarOperator>
</RangeExpressions>
</Prefix>
</SeekKeys>
</SeekPredicateNew>
</SeekPredicates>
</IndexScan>
</RelOp>
</Sort>
</RelOp>
</Segment>
</RelOp>
</SequenceProject>
</RelOp>
<Predicate>
<ScalarOperator ScalarString="[Expr1007]=(1)">
<Compare CompareOp="EQ">
<ScalarOperator>
<Identifier>
<ColumnReference Column="Expr1007" />
</Identifier>
</ScalarOperator>
<ScalarOperator>
<Const ConstValue="(1)" />
</ScalarOperator>
</Compare>
</ScalarOperator>
</Predicate>
</Filter>
</RelOp>
</NestedLoops>
</RelOp>
</StreamAggregate>
</RelOp>
</ComputeScalar>
</RelOp>
</QueryPlan>
</StmtSimple>
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>
JsonProperty
属性,那么创建自己的ContractResolver很简单...
IContractResolver接口提供了一种自定义JsonSerializer如何将.NET对象序列化和反序列化为JSON的方式,而无需在类上放置属性。
赞:
Order
实施:
private class SortedPropertiesContractResolver : DefaultContractResolver
{
// use a static instance for optimal performance
static SortedPropertiesContractResolver instance;
static SortedPropertiesContractResolver() { instance = new SortedPropertiesContractResolver(); }
public static SortedPropertiesContractResolver Instance { get { return instance; } }
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
if (properties != null)
return properties.OrderBy(p => p.UnderlyingName).ToList();
return properties;
}
}
答案 6 :(得分:1)
这对普通的类,字典和ExpandoObject(动态对象)也适用。
class OrderedPropertiesContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
{
var props = base.CreateProperties(type, memberSerialization);
return props.OrderBy(p => p.PropertyName).ToList();
}
}
class OrderedExpandoPropertiesConverter : ExpandoObjectConverter
{
public override bool CanWrite
{
get { return true; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var expando = (IDictionary<string, object>)value;
var orderedDictionary = expando.OrderBy(x => x.Key).ToDictionary(t => t.Key, t => t.Value);
serializer.Serialize(writer, orderedDictionary);
}
}
var settings = new JsonSerializerSettings
{
ContractResolver = new OrderedPropertiesContractResolver(),
Converters = { new OrderedExpandoPropertiesConverter() }
};
var serializedString = JsonConvert.SerializeObject(obj, settings);
答案 7 :(得分:0)
以下递归方法使用反射对现有JObject
实例上的内部令牌列表进行排序,而不是创建全新的已排序对象图。此代码依赖于内部Json.NET实现细节,不应在生产中使用。
void SortProperties(JToken token)
{
var obj = token as JObject;
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(new ComparisonComparer(
(x, y) =>
{
var xProp = x as JProperty;
var yProp = y as JProperty;
return xProp != null && yProp != null
? string.Compare(xProp.Name, yProp.Name)
: 0;
}));
}
foreach (var child in token.Children())
{
SortProperties(child);
}
}
答案 8 :(得分:0)
实际上,由于我的Object已经是一个JObject,我使用了以下解决方案:
public class SortedJObject : JObject
{
public SortedJObject(JObject other)
{
var pairs = new List<KeyValuePair<string, JToken>>();
foreach (var pair in other)
{
pairs.Add(pair);
}
pairs.OrderBy(p => p.Key).ForEach(pair => this[pair.Key] = pair.Value);
}
}
然后像这样使用它:
string serializedObj = JsonConvert.SerializeObject(new SortedJObject(dataObject));
答案 9 :(得分:0)
如果您控制(即写入)该类,则按字母顺序放置属性,并在调用JsonConvert.SerializeObject()
时按字母顺序进行序列化。
答案 10 :(得分:0)
我想序列化一个complex对象,并保持属性在代码中定义的顺序。我不能只添加[JsonProperty(Order = 1)]
,因为类本身不在我的范围内。
此解决方案还考虑到在基类中定义的属性应具有更高的优先级。
这可能不是防弹的,因为没有定义MetaDataAttribute
确保正确顺序的任何地方,但是似乎可行。对于我的用例,这是可以的。因为我只想维护自动生成的配置文件的可读性。
public class PersonWithAge : Person
{
public int Age { get; set; }
}
public class Person
{
public string Name { get; set; }
}
public string GetJson()
{
var thequeen = new PersonWithAge { Name = "Elisabeth", Age = Int32.MaxValue };
var settings = new JsonSerializerSettings()
{
ContractResolver = new MetadataTokenContractResolver(),
};
return JsonConvert.SerializeObject(
thequeen, Newtonsoft.Json.Formatting.Indented, settings
);
}
public class MetadataTokenContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(
Type type, MemberSerialization memberSerialization)
{
var props = type
.GetProperties(BindingFlags.Instance
| BindingFlags.Public
| BindingFlags.NonPublic
).ToDictionary(k => k.Name, v =>
{
// first value: declaring type
var classIndex = 0;
var t = type;
while (t != v.DeclaringType)
{
classIndex++;
t = type.BaseType;
}
return Tuple.Create(classIndex, v.MetadataToken);
});
return base.CreateProperties(type, memberSerialization)
.OrderByDescending(p => props[p.PropertyName].Item1)
.ThenBy(p => props[p.PropertyName].Item1)
.ToList();
}
}
答案 11 :(得分:0)
如果您只想将单个属性拉到前面而不考虑可能不直观的数字系统,只需使用 int.MinValue
。
[JsonProperty(Order = int.MinValue)]
答案 12 :(得分:-1)
如果您想使用有序字段对API进行全局配置,请结合Mattias Nordberg的答案:
public class OrderedContractResolver : DefaultContractResolver
{
protected override System.Collections.Generic.IList<JsonProperty> CreateProperties(System.Type type, MemberSerialization memberSerialization)
{
return base.CreateProperties(type, memberSerialization).OrderBy(p => p.PropertyName).ToList();
}
}
在这里给出我的答案
答案 13 :(得分:-4)
我通过反射跟踪JsonConvert.SerializeObject(key)
方法调用(其中key是一个IList),并发现调用了JsonSerializerInternalWriter.SerializeList。它需要一个列表并循环通过
for (int i = 0; i < values.Count; i++) { ...
其中values是引入的IList参数。
简短的回答是......不,没有内置的方法来设置JSON字符串中列出字段的顺序。
答案 14 :(得分:-6)