我有一个无效的 JSON,我需要使用 Newtonsoft 进行解析。问题在于,JSON 没有使用正确的数组,而是包含数组中每个条目的重复属性。
我有一些工作代码,但真的不确定这是要走的路还是有更简单的方法?
无效的 JSON:
{
"Quotes": {
"Quote": {
"Text": "Hi"
},
"Quote": {
"Text": "Hello"
}
}
}
我试图序列化的对象:class MyTestObject
{
[JsonConverter(typeof(NewtonsoftQuoteListConverter))]
public IEnumerable<Quote> Quotes { get; set; }
}
class Quote
{
public string Text { get; set; }
}
JsonConverter
的读取方法public override IEnumerable<Quote> ReadJson(JsonReader reader, Type objectType, IEnumerable<Quote> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var quotes = new List<Quote>();
while (reader.Read())
{
if (reader.Path.Equals("quotes", StringComparison.OrdinalIgnoreCase) && reader.TokenType == JsonToken.EndObject)
{
// This is the end of the Quotes block. We've parsed the entire object. Stop reading.
break;
}
if (reader.Path.Equals("quotes.quote", StringComparison.OrdinalIgnoreCase) && reader.TokenType == JsonToken.StartObject)
{
// This is the start of a new Quote object. Parse it.
quotes.Add(serializer.Deserialize<Quote>(reader));
}
}
return quotes;
}
我只需要使用重复键读取 JSON,而不是写入。
最佳答案
我可以看到您的转换器存在一些问题:
MyTestObject
时您的转换器将无法工作嵌入在一些更高级别的容器中。事实上,它可能会让读者定位不正确。existingValue
当存在时,这在反序列化 get-only 集合属性时是必需的。作为替代方案,您可以利用以下事实:当在 JSON 中多次遇到该属性时,Json.NET 将多次调用该属性的 setter 来累积
"Quote"
。 DTO 中具有仅设置代理属性的属性值像这样:class NewtonsoftQuoteListConverter : JsonConverter<IEnumerable<Quote>>
{
class DTO
{
public ICollection<Quote> Quotes { get; init; }
public Quote Quote { set => Quotes.Add(value); }
}
public override IEnumerable<Quote> ReadJson(JsonReader reader, Type objectType, IEnumerable<Quote> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return null;
var dto = new DTO { Quotes = existingValue is ICollection<Quote> l && !l.IsReadOnly ? l : new List<Quote>() }; // Reuse existing value if possible
serializer.Populate(reader, dto);
return dto.Quotes;
}
public override bool CanWrite => true; // Replace with false if you don't need custom serialization.
public override void WriteJson(JsonWriter writer, IEnumerable<Quote> value, JsonSerializer serializer)
{
// Handle naming strategies.
var name = ((JsonObjectContract)serializer.ContractResolver.ResolveContract(typeof(DTO))).Properties.Where(p => p.UnderlyingName == nameof(DTO.Quote)).First().PropertyName;
writer.WriteStartObject();
foreach (var item in value)
{
writer.WritePropertyName(name);
serializer.Serialize(writer, item);
}
writer.WriteEndObject();
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
通过使用 DTO,可以考虑当前的命名约定。如果您不需要自定义序列化,请覆盖
CanWrite
并返回 false
.演示 fiddle here .
关于c# - 使用自定义 Newtonsoft JSON 转换器解析具有重复键的 JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69452917/