下面是我的场景:
我正在对一个对象图进行扁平化和序列化。最终的结果是,我将把每个类型的所有对象收集到该类型的数组中,并为每个类型序列化一个数组。但是,当我遍历对象图时,我需要序列化(遍历规则很复杂,需要反射才能读取属性——所以对图进行多次遍历是非常不可取的——我想使用JSON.Net的遍历,这样它的属性也可以用来控制遍历)。
假设我有TypeA
对象和TypeB
对象,假设TypeA
对象可以具有类型为TypeB
的属性,TypeB
对象可以具有类别为TypeA
的属性(不一定是倒数!)。假设我的对象图是这样的:
TypeA aInst1 = new TypeA {
Id = 1,
MyB = new TypeB {
Id = 101,
MyA = new TypeA {
Id = 2,
MyB = null
}
}
}
我最终想要的是:
{
typeAs: [
{
id: 1,
myB: 101
},
{
id: 2,
myB: null
}
],
typeBs: [
{
id: 101,
myA: 2
}
]
}
这里的关键是:当我序列化TypeA(比如TypeAConverter
)时,我会将必要的TypeB对象添加到TypeB哈希集,以确保它们被序列化(很容易)——但在遍历TypeB时,我需要做相反的操作——但如果我线性地这样做,我将已经序列化了TypeA,并且不能向后跳添加新发现的对象。
现在,我已经知道我必须处理很多事情(遍历深度、多次传递、首先获得对根对象的引用以到达TypeXs HashSets),其中大部分我认为我已经解决了。
我不确定如何处理序列化的无序性。到目前为止,我的计划是为每种类型旋转多个JsonWriters
,然后用WriteRaw
将它们的输出附加到主文档/编写器中。但要做到这一点,我必须将自己的范围缩小到JsonTextWriter
,因为更抽象的JsonWriter
类似乎没有访问序列化输出的方法。例如,我宁愿不限制我的用户使用BsonWriter
(如果这是一个实际的目标……似乎是这样的话)。
顺便说一句,我并没有想出这种格式,我只是试图实现它——以尽可能通用的方式。
编辑
对于一些额外的背景,我已经声明了一个接口(称为IModel
),用户将使用它来"标记"任何可以由该序列化库专门处理的类——有一个ModelConverter
可以捕获任何IModel
实例以应用这些特殊规则。我还有一个根对象(类Payload
),用户必须使用它来放置他们的IModel(并设置一些可选配置)。Payload
对象跟踪TypeXs
阵列(确切地说,在Dictionary<Type, ISet<object>>
中),我有一个public static Dictionary<System.Runtime.Serialization.StreamingContext, Payload>
,它可以进行备份(使用ModelConverter
可用的serializer.Context
),所以这就是我能够进行备份并在事后添加这些对象的方式。
值得一提的是,我使用多个JsonTextWriter
(每个类型一个)的计划奏效了,尽管这有点笨拙,我仍然宁愿找到一种方法,不把我锁定在一个JsonWriter
实现中。
这是我实现的核心…很明显,整个解决方案扩展到两个JsonConverters
和其他几个类,但这是我创建、跟踪和使用每种类型的JsonTextWriter
s:的地方
// Handle Appendices
/* This is a bit messy, because we may add items of a given type to the
* set we are currently processing. Not only is this an issue because you
* can't modify a set while you're enumerating it (hence why we make a
* copy first), but we need to catch the newly added objects and process
* them as well. So, we have to keep making passes until we detect that
* we haven't added any new objects to any of the appendices.
*/
Dictionary<Type, ISet<object>>
processed = new Dictionary<Type,ISet<object>>(),
toBeProcessed = new Dictionary<Type,ISet<object>>(); // is this actually necessary?
/* On top of that, we need a new JsonWriter for each appendix--because we
* may write objects of type A, then while processing type B find that
* we need to write more objects of type A! So we can't keep appending
* to the same writer.
*/
/* Oh, and we have to keep a reference to the TextWriter of the JsonWriter
* because there's no member to get it back out again. ?!?
* */
Dictionary<Type, KeyValuePair<JsonWriter,StringWriter>> writers = new Dictionary<Type,KeyValuePair<JsonWriter,StringWriter>>();
int numAdditions;
do
{
numAdditions = 0;
foreach (KeyValuePair<Type, ISet<object>> apair in payload.Appendices)
{
Type type = apair.Key;
ISet<object> appendix = apair.Value;
JsonWriter jw;
if (writers.ContainsKey(type))
{
jw = writers[type].Key;
}
else
{
// Setup and start the writer for this type...
StringWriter sw = new StringWriter();
jw = new JsonTextWriter(sw);
writers[type] = new KeyValuePair<JsonWriter, StringWriter>(jw, sw);
jw.WriteStartArray();
}
HashSet<object> tbp;
if (processed.ContainsKey(type))
{
toBeProcessed[type] = tbp = new HashSet<object>(appendix.Except(processed[type]));
}
else
{
toBeProcessed[type] = tbp = new HashSet<object>(appendix);
processed[type] = new HashSet<object>();
}
if (tbp.Count > 0)
{
numAdditions += tbp.Count;
foreach (object obj in tbp)
{
serializer.Serialize(jw, obj); // Note, not writer, but jw--we write each type to its own JsonWriter and combine them later.
}
processed[type].UnionWith(tbp);
}
//TODO: Add traversal depth limiter!
}
} while (numAdditions > 0);
if (payload.Appendices.Count > 0)
{
writer.WritePropertyName("linked");
writer.WriteStartObject();
// Okay, we should have captured everything now. Now combine the type writers into the main writer...
foreach (KeyValuePair<Type, KeyValuePair<JsonWriter, StringWriter>> apair in writers)
{
apair.Value.Key.WriteEnd(); // close off the array
writer.WritePropertyName(apair.Key.Name);
writer.WriteRawValue(apair.Value.Value.ToString()); // write the contents of the type JsonWriter's StringWriter to the main JsonWriter
}
writer.WriteEndObject();
}
我知道我对Dictionary<Type, KeyValuePair<JsonWriter,StringWriter>>
的使用是一个令人讨厌的黑客攻击。如果有人不能告诉我从JsonWriter中取回StringWriter的方法,我将为这对或其他东西创建一个结构。我想我可以创建一个结构来保存processed
、toBeProcessed
、jsonWriter
和textWriter
,并将它们全部放在一个由Type
键控的Dictionary
中。嗯…