Очистка XML на основе XSD в C # - PullRequest
0 голосов
/ 03 мая 2018

Как очистить файл XML, удалив все элементы, отсутствующие в предоставленном XSD?

Это не работает:

public static void Main()
{
    XmlTextReader xsdReader = new XmlTextReader(@"books.xsd");
    XmlSchema schema = XmlSchema.Read(xsdReader, null);

    XmlReaderSettings settings = new XmlReaderSettings();
    settings.Schemas.Add(schema);
    settings.ValidationType = ValidationType.Schema;
    settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);

    XmlReader xmlReader = XmlReader.Create(@"books.xml", settings);
    XmlWriter xmlWriter = XmlWriter.Create(@"books_clean.xml");
    xmlWriter.WriteNode(xmlReader, true);
    xmlWriter.Close();
    xmlReader.Close();
}
private static void ValidationCallBack(object sender, ValidationEventArgs args)
{
    ((XmlReader)sender).Skip();
}

Когда я использую вышеупомянутое, вместо удаления всех «нежелательных» тегов, он удаляет только первый нежелательный тег и оставляет второй. Что касается того, почему мне нужно принять этот файл, я использую старый экземпляр SQLServer 2012, который требует XML, чтобы точно соответствовать XSD, даже если дополнительные элементы в XML не используются приложением. У меня нет контроля над исходным XML, который предоставляется сторонним инструментом с неопубликованным XSD.

Файлы примеров:
Books.xsd

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="bookstore">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="book" maxOccurs="unbounded" minOccurs="0">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:string" name="title"/>
              <xs:element type="xs:float" name="price"/>
            </xs:sequence>
            <xs:attribute type="xs:string" name="genre" use="optional"/>
            <xs:attribute type="xs:string" name="ISBN" use="optional"/>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Books.xml

<bookstore>
  <book genre='novel' ISBN='10-861003-324'>
    <title>The Handmaid's Tale</title>
    <price>19.95</price>
    <junk>skdjgklsdg</junk>
    <junk2>skdjgklsdg</junk2>
  </book>
  <book genre='novel' ISBN='1-861001-57-5'>
    <title>Pride And Prejudice</title>
    <price>24.95</price>
    <junk>skdjgssklsdg</junk>
  </book>
</bookstore>

Код в основном скопирован из: Проверка XML на XSD со ссылкой в ​​C #

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

Ниже успешно удаляются все ненужные теги из предоставленных примеров. Второй тег xsl: template применяется первым и соответствует всему, кроме специально перечисленных в белом списке тегов. Затем первый тег xsl: template записывает копию узлов в XmlWriter.

Код:

public static void Main()
{
    XmlReader xmlReader = XmlReader.Create("books.xml");
    XslCompiledTransform myXslTrans = new XslCompiledTransform();
    myXslTrans.Load("books.xslt");
    XmlTextWriter myWriter = new XmlTextWriter("books_clean.xml", null);
    myXslTrans.Transform(xmlReader, null, myWriter);
    xmlReader.Close();
    myWriter.Close();
}

books.xslt

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:mode streamable="yes"/>
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="*[
  not(name()='bookstore') and
  not(name()='book') and
  not(name()='title') and
  not(name()='price')
  ]" />
</xsl:stylesheet>
0 голосов
/ 04 мая 2018

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

Если подход удаления неизвестных элементов выглядит полезным, вы можете сделать это следующим образом:

(a) написать таблицу стилей XSLT, которая извлекает все имена элементов из схемы, ища объявления xs:element[@name], генерируя документ в формате:

<allowedElements>
  <allow name="book" namespace=""/>
  <allow name="isbn" namespace=""/>
</allowedElement>

(b) написать вторую (стримбируемую) таблицу стилей XSLT:

<xsl:transform version="3.0" xmlns:xsl="....">
  <xsl:mode on-no-match="shallow-copy" streamable="yes"/>
  <xsl:key name="k" match="allow" use="@name, @namespace" composite="yes"/>
  <xsl:template match="*[not(key('k', (local-name(), namespace-uri()), doc('allowed-elements.xml'))]"/>
</xsl:transform> 
...