Как получить LinqToXSD для правильного вывода объявлений префиксов пространства имен?

Я экспериментирую с созданием классов привязки данных XML с LinqToXSD и схемой XML, содержащей ряд импортированных схем. Все схемы расположены здесь.

Для этого я использовал следующий документ корневой схемы:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:TmatsG="http://www.spiraltechinc.com/tmats/106-13/TmatsG" elementFormDefault="unqualified">
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon" schemaLocation="TmatsCommonTypes.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsC" schemaLocation="TmatsCGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsD" schemaLocation="TmatsDGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsG" schemaLocation="TmatsGGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsH" schemaLocation="TmatsHGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsM" schemaLocation="TmatsMGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsP" schemaLocation="TmatsPGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsR" schemaLocation="TmatsRGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsS" schemaLocation="TmatsSGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsT" schemaLocation="TmatsTGroup.xsd"/>
    <xs:import namespace="http://www.spiraltechinc.com/tmats/106-13/TmatsV" schemaLocation="TmatsVGroup.xsd"/>
    <xs:element name="Tmats" type="TmatsG:Tmats">
        <xs:annotation>
            <xs:documentation>Tmats Root</xs:documentation>
        </xs:annotation>
    </xs:element>
</xs:schema>

Я создал классы, используя Linq to XSD. Затем я написал следующий тест:

[TestMethod()]
public void TmatsXmlExample4()
{
    Tmats tmats = new Tmats
    {
        ProgramName = "My Program",
        OriginationDate = DateTime.Now,
    };
    tmats.PointOfContact.Add(new PointOfContactType
    {
         Address = "12345 Anywhere Street",
         Agency = "My Agency",
         Name = "Robert Harvey",
         Telephone = "111-222-3333"
    });
    Debug.Print(tmats.ToString());
}

Я ожидал вывод, который выглядел примерно так:

<Tmats>
  <TmatsG:ProgramName>My Program</TmatsG:ProgramName>
  <TmatsG:OriginationDate>2012-05-09-07:00</TmatsG:OriginationDate>
  <TmatsG:PointOfContact>
    <TmatsCommon:Name>Robert Harvey</TmatsCommon:Name>
   <TmatsCommon:Agency>My Agency</TmatsCommon:Agency>
    <TmatsCommon:Address>12345 Anywhere Street</TmatsCommon:Address>
    <TmatsCommon:Telephone>111-222-3333</TmatsCommon:Telephone>
  </TmatsG:PointOfContact>
</Tmats>

Вместо этого я получил следующее:

<Tmats>
  <ProgramName xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsG">My Program</ProgramName>
  <OriginationDate xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsG">2012-05-09-07:00</OriginationDate>
  <PointOfContact xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsG">
    <Name xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">Robert Harvey</Name>
    <Agency xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">My Agency</Agency>
    <Address xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">12345 Anywhere Street</Address>
    <Telephone xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">111-222-3333</Telephone>
  </PointOfContact>
</Tmats>

Есть ли способ получить LinqToXSD для получения ожидаемого результата?

1 ответ

Вы должны сопоставить каждую из импортированных схем:

<?xml version="1.0"?>
    <xs:schema 
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:TmatsCommon="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon"
        xmlns:TmatsC="http://www.spiraltechinc.com/tmats/106-13/TmatsC"
        xmlns:TmatsD="http://www.spiraltechinc.com/tmats/106-13/TmatsD"
        xmlns:TmatsG="http://www.spiraltechinc.com/tmats/106-13/TmatsG"
        xmlns:TmatsH="http://www.spiraltechinc.com/tmats/106-13/TmatsH"
        xmlns:TmatsM="http://www.spiraltechinc.com/tmats/106-13/TmatsM"
        … 
        elementFormDefault="unqualified">

elementFormDefault применяется только к схеме, в которой он находится, и он не переопределяет настройки ни при каких включениях или импорте.

Если вы хотите скрыть пространства имен, тогда все схемы должны указывать elementFormDefault = " unqualified ". Точно так же, если вы хотите представить пространства имен, каждая схема должна указывать elementFormDefault="qualised"

ОБНОВЛЕНО после проверки юнит-тестов:

Ваш вклад:

<Tmats 
    xmlns:TmatsG="http://www.spiraltechinc.com/tmats/106-13/TmatsG"
    xmlns:TmatsCommon="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">
    <TmatsG:ProgramName>My Project</TmatsG:ProgramName> 
    <TmatsG:OriginationDate>2012-05-15</TmatsG:OriginationDate> 

Ваш вывод:

<Tmats> 
    <Tmats 
        xmlns:TmatsG="http://www.spiraltechinc.com/tmats/106-13/TmatsG"
        xmlns:TmatsCommon="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">
        <TmatsG:ProgramName>My Project</TmatsG:ProgramName> 
        <TmatsG:OriginationDate>2012-05-15</TmatsG:OriginationDate> 

Выдающаяся проблема - дублирование тега - все выглядит хорошо для меня, все еще изо всех сил пытаясь понять, почему это происходит.

ОБНОВЛЕНИЕ понедельник:

Я думаю, что есть ошибка в инструменте LinqToXSD - я пробежал каждую комбинацию, которую могу придумать и не могу последовательно обойти вашу проблему, однако... Мне удалось исправить <Tmats> проблема дублирования:

В вашем файле XmlHelper измените оператор return:

System.Xml.Linq.XDocument xd = System.Xml.Linq.XDocument.Parse(sb.ToString());
return xd.Root.FirstNode.ToString();

Я знаю, что это взлом, но это решает проблему, и ваш LoopbackTest проходит.

Вы не получите никаких префиксов, если создаете элементы с использованием класса Tmats, я пробовал различные комбинации атрибутов, и лучшее, что я мог сделать, это повторно присоединить пространства имен. Если вы обмениваетесь информацией с внешней системой, тогда у меня есть исправление:

  1. Используйте свой объект Tmats в своем коде,
  2. Сериализуйте это с помощью пространств имен,
  3. Запустите его через XSLT, чтобы сопоставить ns с префиксами.

Я знаю, что это неуклюже, но я считаю, что это лучшее, что вам не хватит, чтобы на самом деле исправить код LinqToXSD.

XSLT для сопоставления пространств имен с префиксами (вам нужно сохранить набор пространств имен в объявлении 'stylesheet', а также в 'mapper':

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:mapper="http://mapper"
    xmlns:TmatsCommon="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon"
    xmlns:TmatsC="http://www.spiraltechinc.com/tmats/106-13/TmatsC"
    xmlns:TmatsD="http://www.spiraltechinc.com/tmats/106-13/TmatsD"
    xmlns:TmatsG="http://www.spiraltechinc.com/tmats/106-13/TmatsG"
    xmlns:TmatsH="http://www.spiraltechinc.com/tmats/106-13/TmatsH"
    xmlns:TmatsM="http://www.spiraltechinc.com/tmats/106-13/TmatsM"
    xmlns:TmatsP="http://www.spiraltechinc.com/tmats/106-13/TmatsP"
    xmlns:TmatsR="http://www.spiraltechinc.com/tmats/106-13/TmatsR"
    xmlns:TmatsS="http://www.spiraltechinc.com/tmats/106-13/TmatsS"
    xmlns:TmatsT="http://www.spiraltechinc.com/tmats/106-13/TmatsT"
    xmlns:TmatsV="http://www.spiraltechinc.com/tmats/106-13/TmatsV">
  <xsl:output omit-xml-declaration="no" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <mapper:namespaces>
    <ns prefix="TmatsCommon" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon"/>
    <ns prefix="TmatsC" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsC"/>
    <ns prefix="TmatsD" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsD"/>
    <ns prefix="TmatsG" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsG"/>
    <ns prefix="TmatsH" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsH"/>
    <ns prefix="TmatsM" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsM"/>
    <ns prefix="TmatsP" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsP"/>
    <ns prefix="TmatsR" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsR"/>
    <ns prefix="TmatsS" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsS"/>
    <ns prefix="TmatsT" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsT"/>
    <ns prefix="TmatsV" uri="http://www.spiraltechinc.com/tmats/106-13/TmatsV"/>
  </mapper:namespaces>
  <xsl:template match="node()|@*">
     <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
 </xsl:template>

 <xsl:template match="*[namespace-uri()=document('')/*/mapper:namespaces/*/@uri]">
  <xsl:variable name="vNS" select="document('')/*/mapper:namespaces/*[@uri=namespace-uri(current())]"/>
  <xsl:element name="{$vNS/@prefix}:{local-name()}" namespace="{namespace-uri()}" >
   <xsl:copy-of select="namespace::*[not(. = namespace-uri(current()))]"/>
   <xsl:copy-of select="@*"/>
   <xsl:apply-templates/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

Производит:

<Tmats>
  <TmatsG:ProgramName xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsG">My Program</TmatsG:ProgramName>
  <TmatsG:OriginationDate xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsG">2012-05-09-07:00</TmatsG:OriginationDate>
  <TmatsG:PointOfContact xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsG">
    <TmatsCommon:Name xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">Robert Harvey</TmatsCommon:Name>
    <TmatsCommon:Agency xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">My Agency</TmatsCommon:Agency>
    <TmatsCommon:Address xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">12345 Anywhere Street</TmatsCommon:Address>
    <TmatsCommon:Telephone xmlns="http://www.spiraltechinc.com/tmats/106-13/TmatsCommon">111-222-3333</TmatsCommon:Telephone>
  </TmatsG:PointOfContact>
</Tmats>

Хорошо, так что это далеко от идеала, но ваш код прекрасно работает внутри проекта, только если вам нужно взаимодействовать с другими людьми, которым нужно исправить вывод xml (не забудьте изменить elementFormDefault="qualised) (или удалить это) в вашем XSD) - если вы кешируете XSLT как XslCompiledTransform Вы едва заметили бы это вообще.

Другие вопросы по тегам