时间:2010-07-25 21:01:21

标签: c# json json.net

15 个答案:

答案 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) &#xD;&#xA;FROM Profile p LEFT OUTER JOIN &#xD;&#xA; ( &#xD;&#xA; SELECT hispKey, hisData FROM (&#xD;&#xA; SELECT hispKey, hisData, ROW_NUMBER() OVER (PARTITION BY hispKey ORDER BY hisIndex DESC) RowID &#xD;&#xA; FROM HistoryData WHERE hisgKey = 318 AND hisType = 'SeveranceFinal' &#xD;&#xA; ) t WHERE RowID = 1&#xD;&#xA; ) h ON p.pKey = h.hispKey&#xD;&#xA;WHERE p.pgKey = 318&#xD;&#xA;" 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();
    }
}

在这里给出我的答案

How to force ASP.NET Web API to always return JSON?

答案 13 :(得分:-4)

我通过反射跟踪JsonConvert.SerializeObject(key)方法调用(其中key是一个IList),并发现调用了JsonSerializerInternalWriter.SerializeList。它需要一个列表并循环通过

for (int i = 0; i < values.Count; i++) { ...

其中values是引入的IList参数。

简短的回答是......不,没有内置的方法来设置JSON字符串中列出字段的顺序。

答案 14 :(得分:-6)