Перемещение произвольного графа объектов С# с использованием XPath/применения XSL-преобразований

Я искал компонент, который позволил бы мне передать произвольный объект С# на XSL-преобразование.

Наивный способ сделать это - сериализировать граф объекта с помощью XmlSerializer; однако, если у вас большой граф объектов, это может вызвать проблемы с производительностью. Такие проблемы, как круговые ссылки, ленивая загрузка, прокси и т.д., Могут еще больше загрязнять воды здесь.

Лучший подход заключается в том, чтобы иметь какой-то класс адаптера, который реализует IXPathNavigable и XPathNavigator. Одним из таких примеров, с которыми я столкнулся, является ObjectXPathNavigator от Byte-Force, однако большая часть его ключевой документации находится на русском языке, и мой первоначальный тесты показывают, что у него есть несколько особенностей и особенностей.

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

Ответ 1

Там (очень) старая статья MSDN под названием XPath Querying Over Objects с ObjectXPathNavigator, которая реализует аналогичный класс (также достаточно интересна ObjectXPathNavigator), Я использовал это много лет назад, чтобы запросить некоторые данные из Visual SourceSafe и создать RSS-канал из журнала изменений, и он работал достаточно хорошо. Однако я не использовал XSLT, поэтому я не уверен, что это работает или нет. Также обратите внимание, что он был написан для Framework 1.0, поэтому вам может потребоваться обновить его для более поздних фреймов. Кроме того, могут быть лучшие способы сделать это сейчас, но это даст вам отправную точку (и в статье хорошо написано, как это работает).

Ответ 2

Звучит так, как будто проблема, которую вы пытаетесь решить, довольно интересна.

На первый взгляд, я бы предложил написать собственную реализацию потокового XPathNavigator - есть только 20-нечетные методы для записи, и ни одна из них не имеет особенно сложной подписи.

Наивная реализация с использованием не кэшированного отражения будет медленной (ish), но будет хорошо работать как доказательство концепции, и вы могли бы внести изменения для повышения производительности, если/когда это стало проблемой.

Однако...

... Я думаю, вы можете столкнуться с некоторыми трудностями, которые вытекают из вашего подхода, а не из какой-либо детали реализации.

XML файл (по своей природе) представляет собой простую иерархию элементов и атрибутов - в графе node нет циклов (ака циклов).

Выражение XPath может включать оператор "//", что в широком смысле означает поиск неограниченной глубины. (Для точного определения см. Раздел 2.5 XPath 1.0.)

Если вы применили такое выражение к графу объектов с перекрестными ссылками (так называемыми объектными циклами), тогда вы рискуете, что оценщик XPath перейдет в бесконечный цикл, поскольку он попытался рекурсивно перечислить эффективно бесконечный граф.

Возможно, вы сможете обойти эту проблему, как-то отслеживая родительские узлы в XPathNavigator и бросая исключение, если цикл обнаружен, но я не уверен, насколько это возможно.

Ответ 3

Так как граф объекта может быть циклическим, вы не можете сделать из него структуру на основе дерева. Лучше всего представить граф объекта с помощью простейших компонентов: узлов и векторов.

В частности, сделайте каждый node (объект) элементом с уникальным идентификатором (возможно, предоставленным методом С# GetHashCode()?). Ссылки на другие объекты (векторы) будут обрабатываться путем ссылки на идентификатор объекта.

Примеры классов (обратите внимание, что я не знаю С#, поэтому мой синтаксис может быть немного выключен):

public class SomeType {
   public int myInt  { get; set; }
}

public class AnotherType {
   public string myString { get; set; }
   public SomeType mySomeType { get; set; }
}

public class LastType {
   public SomeType mySomeType { get; set; }
   public AnotherType myAnotherType { get; set; }
}

public class UserTypes{
    static void Main()
    {
        LastType lt = new LastType();
        SomeType st = new SomeType();
        AnotherType atype = new AnotherType();

        st.myInt = 7;
        atype.myString = "BOB";
        atype.mySomeType = st;
        lt.mySomeType = st;
        lt.myAnotherType = atype;

        string xmlOutput = YourAwesomeFunction(lt);
    }
}

Тогда мы ожидаем, что значение xmlOutput будет чем-то вроде этого (обратите внимание, что выбранные значения ID полностью синтетические):

<ObjectMap>
 <LastType id="0">
   <mySomeType idref="1" />
   <myAnotherType idref="2" />
 </LastType>

 <SomeType id="1">
  <myInt>7</myInt>
 </SomeType>

 <AnotherType id="2">
  <myString>BOB</myString>
  <mySomeType idref="1" />
 </AnotherType>
</ObjectMap>