Определить тип DataContract из XElement

Я использую пространство имен System.Runtime.Serialization за пределами WCF и хотел бы узнать, какой тип представляет элемент — есть ли преобразователь имен? Мне бы очень хотелось что-то вроде:

Type ResolveName(XmlQualifiedName typeName);

Я вижу, что во внутренней работе WCF есть что-то подобное, но я не могу найти общедоступного.

Спасибо!


person Matt DeKrey    schedule 09.07.2011    source источник


Ответы (2)


Частью смысла сериализации на основе контрактов в стиле DataContractSerialization является то, что она не привязана к конкретным типам (примечание: NetDataContractSerializer не разделяет этого). Таким образом, типов-кандидатов может быть ноль, один или много. Что бы оно выбрало?

Так что нет; AFAIK это принципиально невозможно. Возможно, вы могли бы использовать отражение для всех типов во всех загруженных сборках, ища [DataContract] и применяя тесты вручную, конечно.

Обычно у вас есть существующая информация о типе (либо корневой тип, либо текущий тип), поэтому вам нужно рассмотреть только небольшой набор типов-кандидатов (KnownType и т. д.).

person Marc Gravell    schedule 09.07.2011
comment
Хм, это справедливо - я бы не возражал против того, чтобы мне даже предоставили список типов-кандидатов или приняли список известных типов, как это делает DataContractSerializer. Суть в том, что я не хочу десериализовать его сейчас, но я хочу знать, что было бы загружено, если бы я это сделал. (Отложенная загрузка материала.) Я работаю над решением, которое использует Reflection (тьфу) и внутренний класс DataContract. Спасибо, что заглянули! - person Matt DeKrey; 09.07.2011
comment
Ну, я ожидаю довольно большое количество (возможно, сотни тысяч) этих объектов в приложении, над которым я работаю, и я буду просто хранить путь к объекту (сериализованному на жесткий диск, а не как WCF контракт), так как я ожидаю, что приложение будет работать только с меньшим (скажем, сотнями) подмножеством за раз. Мы используем сериализацию DataContract, чтобы XPath можно было использовать для анализа файла с помощью внешних инструментов, и это экономит время на изучение/написание нового сериализатора. - person Matt DeKrey; 09.07.2011
comment
@Matt, значит, я не могу заинтересовать вас другим сериализатором :) Однако я подозреваю, что ваш сценарий технически сложнее, чем кажется. Одно и то же имя элемента в XML может относиться к разным типам в разных контекстах. - person Marc Gravell; 09.07.2011
comment
Спасибо, Марк - да, я знаю, что это может относиться к разным типам (что является частью причины, по которой я использую пространство имен + имя, а не явно ввожу имена типов с указанием сборки) - я опубликую свое отражение -основанный образец, как только я закончу с ним. Спасибо, что выслушали меня! - person Matt DeKrey; 09.07.2011

Поскольку я все еще думал, что мне нужна эта функциональность, я пошел и написал класс, чтобы получить информацию. Я использовал Impromptu-Interface, чтобы ускорить биты отражения, но весь этот процесс меня раздражает.

public class DataContractNameResolver
{
    private Type TypeOfDataContract = null;
    private Dictionary<System.Xml.XmlQualifiedName, Type> xmlNames = new Dictionary<System.Xml.XmlQualifiedName, Type>();

    internal void PrecacheBaseTypes(IEnumberable<Type> types)
    {
        if (TypeOfDataContract == null)
        {
            TypeOfDataContract = Type.GetType("System.Runtime.Serialization.DataContract, System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
        }

        lock (xmlNames)
        {
            var remaining = new Queue<Type>(types.Except(xmlNames.Values));

            while (remaining.Count > 0)
            {
                Type next = remaining.Dequeue();

                var dc = Impromptu.InvokeMember(TypeOfDataContract.WithStaticContext(), "GetDataContract", next);
                IDataContract result = Impromptu.ActLike<IDataContract>(dc);

                xmlNames.Add(new System.Xml.XmlQualifiedName(result.Name.Value, result.Namespace.Value), next);
            }
        }
    }

    public Type ResolveName(System.Xml.XmlQualifiedName typeName)
    {
        if (xmlNames.ContainsKey(typeName))
        {
            return xmlNames[typeName];
        }
        return null;
    }
}

Использование этого класса будет примерно таким:

var nameResolver = new DataContractNameResolver();
nameResolver.PrecacheBaseTypes(new[] { typeof(SampleOne), typeof(SampleTwo) });
var resultType = nameResolver.ResolveName(...);
person Matt DeKrey    schedule 09.07.2011