一些(外部)天才决定向我们提供XML:
<message_X>
<header>
<foo>Foo</foo>
<bar>Bar</bar>
</header>
<body>
<blah>
<yadda1 />
<yadda2 />
<yadda3 />
<contentX>
<!-- message_X specific content -->
</contentX>
</blah>
</body>
</message_X>
但是,还有其他消息(例如message_Y
和message_Z
)。除了content
节点中的内容之外,所有这些都具有完全相同的基本结构,并且,导致该问题的原因是不同的根节点:
<message_Y>
<header>
<foo>Foo</foo>
<bar>Bar</bar>
</header>
<body>
<blah>
<yadda1 />
<yadda2 />
<yadda3 />
<contentY>
<!-- message_X specific content -->
</contentY>
</blah>
</body>
</message_Y>
为什么根节点不仅仅被命名为<message>
,因为我会这么做,这让我感到困惑。谁想到这个?
我相应地创建了一个抽象类Message
:
public abstract class Message {
public Header Header { get; set; }
public Body Body { get; set; }
}
public class Header {
public string Foo { get; set; }
public string Bar { get; set; }
}
// Etc...
我希望我能做到这一点:
[XmlInclude(typeof(XMessage))]
[XmlInclude(typeof(YMessage))]
public abstract class Message {
// ...
}
[XmlRoot("message_X")]
public class XMessage : Message {
// ...
}
[XmlRoot("message_Y")]
public class YMessage : Message {
// ...
}
但这不起作用:InvalidOperationException: <message_X xmlns=''> was not expected.
。要反序列化,我使用:
var ser = new XmlSerializer(typeof(Message));
using (var sr = new StringReader(xmlString))
return (Message)ser.Deserialize(sr);
我无法控制XML,因此我不希望为X,Y和Z一次又一次地实现此消息。
我将Content
放入Message
并通过指定T等进行继承来整理Message<T>
部分,但这将在以后引起关注。
我还尝试将XmlSerializer Constructor的Message
指定为Type
,将XMessage
和YMessage
指定为ExtraTypes
,但这没有帮助要么。我还尝试过使用带有DataContractSerializer
,DataContract
等注释的KnownType
路线,但这也不起作用。
我希望您能以干净的方式解决此问题的技巧/指针。
答案 0 :(得分:0)
使用@ steve16351的**思想,我编写了以下反序列化器(我对序列化不感兴趣,仅对反序列化感兴趣):
Paths.js
此反序列化器使用public class XmlDeserializer<T>
where T : class
{
// "Globally" caches T => Dictionary<xmlelementnames, type>
private static readonly ConcurrentDictionary<Type, IDictionary<string, Type>> _typecache = new ConcurrentDictionary<Type, IDictionary<string, Type>>();
// We store instances of serializers per type T in this pool so we need not create a new one each time
private static readonly ConcurrentDictionary<Type, XmlSerializer> _serializers = new ConcurrentDictionary<Type, XmlSerializer>();
// And all serializers get the same instance of XmlReaderSettings which, again saves creating objects / garbage collecting.
private static readonly XmlReaderSettings _readersettings = new XmlReaderSettings() { IgnoreWhitespace = true };
// Lookup for current T, with this we keep a reference for the current T in the global cache so we need one less dictionary lookup
private readonly IDictionary<string, Type> _thistypedict;
public XmlDeserializer()
{
// Enumerate T's XmlInclude attributes
var includes = ((IEnumerable<XmlIncludeAttribute>)typeof(T).GetCustomAttributes(typeof(XmlIncludeAttribute), true));
// Get all the mappings
var mappings = includes.Select(a => new
{
a.Type,
((XmlRootAttribute)a.Type.GetCustomAttributes(typeof(XmlRootAttribute), true).FirstOrDefault())?.ElementName
}).Where(m => !string.IsNullOrEmpty(m.ElementName));
// Store all mappings in our current instance and at the same time store the mappings for T in our "global cache"
_thistypedict = _typecache.GetOrAdd(typeof(T), mappings.ToDictionary(v => v.ElementName, v => v.Type));
}
public T Deserialize(string input)
{
// Read our input
using (var stringReader = new StringReader(input))
using (var xmlReader = XmlReader.Create(stringReader, _readersettings))
{
xmlReader.MoveToContent();
// Make sure we know how to deserialize this element
if (!_thistypedict.TryGetValue(xmlReader.Name, out var type))
throw new InvalidOperationException($"Unable to deserialize type '{xmlReader.Name}'");
// Grab serializer from pool or create one if required
var serializer = _serializers.GetOrAdd(type, (t) => new XmlSerializer(t, new XmlRootAttribute(xmlReader.Name)));
// Finally, now deserialize...
return (T)serializer.Deserialize(xmlReader);
}
}
}
属性来确定要使用XmlInclude
属性将哪些类映射到哪个元素名称。使用再简单不过了:
XmlRoot
它对对象进行一些内部的“缓存”和“池化”,以使其对内存/ GC友好并具有良好的性能。因此,这解决了我的每种类型的根节点不同名称的问题。现在,我需要弄清楚如何处理不同的内容节点...
**此后谁因某些未知原因删除了答案...
问题的第二部分,“通用内容”很容易解决:
var ser = new XmlDeserializer<Message>();
ser.Deserialize("<message_X>...");
这只让我想知道我是否可以在根节点上应用相同的想法...