JSON.NET을 사용하여 직렬화 된 JSON 객체에서 필드 순서를 지정하는 방법이 있습니까?
단일 필드가 항상 먼저 표시되도록 지정하면 충분합니다.
JSON.NET을 사용하여 직렬화 된 JSON 객체에서 필드 순서를 지정하는 방법이 있습니까?
단일 필드가 항상 먼저 표시되도록 지정하면 충분합니다.
답변:
지원되는 방법은 JsonProperty
순서를 설정하려는 클래스 특성 에서 속성 을 사용하는 것입니다. 자세한 정보 는 JsonPropertyAttribute 주문 문서 를 읽으십시오 .
패스 JsonProperty
의 Order
값과 시리얼 라이저는 나머지를 다룰 것이다.
[JsonProperty(Order = 1)]
이것은 매우 유사합니다
DataMember(Order = 1)
의 System.Runtime.Serialization
일.
@ kevin-babcock의 중요한 메모는 다음과 같습니다.
... 순서를 1로 설정하면 다른 모든 속성에서 순서를 1보다 크게 설정 한 경우에만 작동합니다. 기본적으로 주문 설정이없는 속성은 -1의 순서로 제공됩니다. 따라서 모든 직렬화 된 속성과 순서를 제공하거나 첫 번째 항목을 -2로 설정해야합니다.
Order
속성을 사용하여 JsonPropertyAttribute
필드를 직렬화 / 직렬화하는 순서를 제어 할 수 있습니다. 그러나 순서를 1로 설정하면 다른 모든 속성에서 순서를 1보다 크게 설정 한 경우에만 작동합니다. 기본적으로 주문 설정이없는 속성은 -1의 순서로 제공됩니다. 따라서 모든 직렬화 된 속성과 순서를 제공하거나 첫 번째 항목을 -2로 설정해야합니다.
JavaScriptSerializer
.
의 메소드를 구현 IContractResolver
하거나 재정 의하여 실제로 순서를 제어 할 수 있습니다 .DefaultContractResolver
CreateProperties
다음 IContractResolver
은 속성을 사전 순으로 정렬하는 간단한 구현의 예입니다 .
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();
}
}
그런 다음 설정을 설정하고 객체를 직렬화하면 JSON 필드가 알파벳 순서로 표시됩니다.
var settings = new JsonSerializerSettings()
{
ContractResolver = new OrderedContractResolver()
};
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
제 경우에는 Mattias의 대답이 효과가 없었습니다. 이 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;
}
}
필자의 경우 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;
}
}
}
이것은 일반 클래스, 사전 및 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);
CreateProperties
사전의 직렬화 중에는 호출되지 않습니다. 기계가 실제로 사전 항목을 통해 구타하는 것에 대한 JSON.net 저장소를 탐색했습니다. override
주문을 위해 다른 사용자 정의에 연결되지 않습니다 . 객체의 열거 자에서 그대로 항목을 가져옵니다. JSON.net을 구성 SortedDictionary
하거나 SortedList
강제로 작성해야합니다. 제안 된 기능 제안 : github.com/JamesNK/Newtonsoft.Json/issues/2270
JsonProperty
Order
모든 클래스 속성에 속성 을 넣고 싶지 않으면 자신의 ContractResolver를 만드는 것이 매우 간단합니다 ...
IContractResolver 인터페이스는 JsonSerializer가 클래스에 속성을 배치하지 않고 .NET 객체를 JSON으로 직렬화 및 직렬화 해제하는 방법을 사용자 정의하는 방법을 제공합니다.
이처럼 :
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;
}
}
도구:
var settings = new JsonSerializerSettings { ContractResolver = SortedPropertiesContractResolver.Instance };
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);
다음 재귀 방법은 리플렉션을 사용하여 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);
}
}
실제로 내 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));
comblex 객체를 직렬화하고 속성 순서를 코드에 정의 된대로 유지하고 싶습니다. 난 그냥 추가 할 수 없습니다[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();
}
}
주문형 필드로 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();
}
}
내 대답은 다음과 같습니다.
최신 정보
난 그냥 downvotes를 보았다. 이 작업을 수행하는 방법은 아래 'Steve'의 답변을 참조하십시오.
실물
JsonConvert.SerializeObject(key)
리플렉션 (키가 IList)을 통해 메소드 호출을 따랐으며 JsonSerializerInternalWriter.SerializeList가 호출되는 것을 발견했습니다. 목록을 가져와 통해 반복합니다.
for (int i = 0; i < values.Count; i++) { ...
여기서 values는 가져온 IList 매개 변수입니다.
짧은 대답은 ... 아니요, JSON 문자열에 필드가 나열되는 순서를 설정하는 방법이 없습니다.
JSON 형식의 필드 순서가 없으므로 순서를 정의하는 것은 의미가 없습니다.
{ id: 1, name: 'John' }
(동일 { name: 'John', id: 1 }
하게 동등한 객체 인스턴스를 나타냄)