2013-05-29 19 views
5

Potrzebuję pomocy przy formatowaniu tego pliku xml do pliku tekstowego o stałej szerokości za pomocą arkusza stylów xsl. Wiem bardzo niewiele o xsl i znalazłem bardzo niewiele informacji online na temat tego, jak można to zrobić.XML do Stała szerokość pliku tekstowego z arkusza stylów xsl

Zasadniczo muszę ten XML

<?xml version="1.0" encoding="UTF-8"?> 
<Report> 
    <table1> 
     <Detail_Collection> 
     <Detail> 
      <SSN>*********</SSN> 
      <DOB>1980/11/11</DOB> 
      <LastName>user</LastName> 
      <FirstName>test</FirstName> 
      <Date>2013/02/26</Date> 
      <Time>14233325</Time> 
      <CurrentStreetAddress1>53 MAIN STREET</CurrentStreetAddress1> 
      <CurrentCity>san diego</CurrentCity> 
      <CurrentState>CA</CurrentState> 
     </Detail_Collection> 
    </table1> 
</Report> 

w tym formacie, wszystkie w tym samym wierszu

*********19801111user   test  201302261423332553 MAIN STREET         san diego   CA 

Są trwałe szerokości

FR TO 
1 9  SSN 
10 17 DOB 
18 33 LastName 
34 46 FirstName 
47 54 Date 
55 62 Time 
63 90 CurrentStreetAddress1 
91 115 CurrentCity 
116 131 CurrentStat 

Wszystkie pomoc jest mile widziana! Z góry dziękuję!

+0

Czy można użyć rozszerzenia takiego jak node-set() lub dodatkowego dokumentu/pliku xml, aby zachować szerokość i kolejność danych wyjściowych? –

Odpowiedz

5

Sekretem tego w XSLT 1.0 jest uświadomienie sobie, że można połączyć "strategię wypełniania" ze "strategią podłańcuchową", aby wydzielić lub odciąć fragment tekstu na pożądaną szerokość. W szczególności, instrukcje XSLT tej formy:

substring(concat('value to pad or cut', '  '), 1, 5) 

... concat gdzie stosuje się, aby dodać liczbę znaków wypełniających do łańcucha i substring służy do ograniczenia całkowitej szerokości, są pomocne. Mając to na uwadze, oto rozwiązanie XSLT 1.0, które zapewnia to, czego oczekujesz.

Należy pamiętać, że w oczekiwanym wyniku niektóre szerokości znaków nie odpowiadają Twoim wymaganiom; na przykład, zgodnie z wymaganiami, <LastName> powinien mieć rozmiar 16 znaków, podczas gdy twój wynik wydaje się być wyłączony o godzinie 13. Powiedziałbym, że moje rozwiązanie poniżej daje oczekiwane rezultaty.

Gdy XSLT:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output omit-xml-declaration="no" indent="yes" method="text"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="Detail"> 
    <xsl:apply-templates /> 
    <xsl:text>&#10;</xsl:text> 
    </xsl:template> 

    <xsl:template match="SSN"> 
    <xsl:value-of 
     select="substring(concat(., '   '), 1, 9)"/> 
    </xsl:template> 

    <xsl:template match="DOB"> 
    <xsl:value-of 
     select="substring(concat(translate(., '/', ''), '  '), 1, 8)"/> 
    </xsl:template> 

    <xsl:template match="LastName"> 
    <xsl:value-of 
     select="substring(concat(., '    '), 1, 16)"/> 
    </xsl:template> 

    <xsl:template match="FirstName"> 
    <xsl:value-of 
     select="substring(concat(., '    '), 1, 13)"/> 
    </xsl:template> 

    <xsl:template match="Date"> 
    <xsl:value-of 
     select="substring(concat(translate(., '/', ''), '  '), 1, 8)"/> 
    </xsl:template> 

    <xsl:template match="Time"> 
    <xsl:value-of 
     select="substring(concat(., ' '), 1, 8)"/> 
    </xsl:template> 

    <xsl:template match="CurrentStreetAddress1"> 
    <xsl:value-of 
     select="substring(concat(., '       '), 1, 28)"/> 
    </xsl:template> 

    <xsl:template match="CurrentCity"> 
    <xsl:value-of 
     select="substring(concat(., '       '), 1, 25)"/> 
    </xsl:template> 

    <xsl:template match="CurrentStat"> 
    <xsl:value-of 
     select="substring(concat(., '    '), 1, 15)"/> 
    </xsl:template> 

</xsl:stylesheet> 

... prowadzony jest przeciwko przewidzianym XML (z </Detail> dodanej, aby dokument dobrze uformowane):

<Report> 
    <table1> 
    <Detail_Collection> 
     <Detail> 
     <SSN>*********</SSN> 
     <DOB>1980/11/11</DOB> 
     <LastName>user</LastName> 
     <FirstName>test</FirstName> 
     <Date>2013/02/26</Date> 
     <Time>14233325</Time> 
     <CurrentStreetAddress1>53 MAIN STREET</CurrentStreetAddress1> 
     <CurrentCity>san diego</CurrentCity> 
     <CurrentState>CA</CurrentState> 
     </Detail> 
    </Detail_Collection> 
    </table1> 
</Report> 

... wymagany jest wynik:

*********19801111user   test   201302261423332553 MAIN STREET    san diego    CA 
+0

To jest idealne, dziękuję. Jeśli istnieje wiele sekcji szczegółowych, po jednej dla każdej osoby, czy istnieje sposób wstawienia podziału linii, aby każda sekcja szczegółów znajdowała się w nowej linii? Dzięki jeszcze raz. – user973671

+0

@ user973671 - świetne pytanie. Właśnie zrobiłem aktualizację, która będzie pasować do scenariusza, który opisujesz. – ABach

6

Oto (moim zdaniem) lite bardziej niezawodne i utrzymaniu wersja:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > 
    <xsl:output method="text" indent="no"/> 

    <xsl:variable name="some_spaces" select="'                 '" /> 

    <xsl:template match="/"> 
     <xsl:apply-templates select="//Detail_Collection/Detail" /> 
    </xsl:template> 

    <xsl:template match="Detail_Collection/Detail"> 
     <xsl:apply-templates mode="format" select="SSN"> 
      <xsl:with-param name="width" select="number(9-1)"/> 
     </xsl:apply-templates> 
     <xsl:apply-templates mode="format_date" select="DOB"> 
      <xsl:with-param name="width" select="number(17-10)"/> 
     </xsl:apply-templates> 
     <xsl:apply-templates mode="format" select="LastName"> 
      <xsl:with-param name="width" select="number(33-18)"/> 
     </xsl:apply-templates> 
     <xsl:apply-templates mode="format" select="FirstName"> 
      <xsl:with-param name="width" select="number(46-34)"/> 
     </xsl:apply-templates> 
     <xsl:apply-templates mode="format_date" select="Date"> 
      <xsl:with-param name="width" select="number(54-47)"/> 
     </xsl:apply-templates> 
     <xsl:apply-templates mode="format" select="Time"> 
      <xsl:with-param name="width" select="number(62-55)"/> 
     </xsl:apply-templates> 
     <xsl:apply-templates mode="format" select="CurrentStreetAddress1"> 
      <xsl:with-param name="width" select="number(90-63)"/> 
     </xsl:apply-templates> 
     <xsl:apply-templates mode="format" select="CurrentCity"> 
      <xsl:with-param name="width" select="number(115-91)"/> 
     </xsl:apply-templates> 
     <xsl:apply-templates mode="format" select="CurrentState"> 
      <xsl:with-param name="width" select="number(131-116)"/> 
     </xsl:apply-templates> 
     <xsl:text>&#10;</xsl:text> 
    </xsl:template> 

    <xsl:template match="node()" mode ="format"> 
     <xsl:param name="width" /> 
     <xsl:value-of select="substring(concat(text(),$some_spaces), 1, $width+1)"/> 
    </xsl:template> 
    <xsl:template match="node()" mode="format_date"> 
     <xsl:param name="width" /> 
     <xsl:value-of select="substring(concat(translate(text(),'/',''),$some_spaces), 1, $width+1)"/> 
    </xsl:template> 

</xsl:stylesheet> 

To stworzy odpowiednią moc nawet jeśli pola w wejściu nie w porządku z wymaganym wyjściu, czy pola brakuje na wejściu. Uważa również, że istnieje więcej niż jeden wpis szczegółowy.

+0

+1 za świetny parametr "szerokość". – ABach

+0

Um, wszystkie szerokości są wyłączone o jeden: jeśli CurrentStreet zajmuje kolumny od 63 do 90, jego szerokość wynosi 28, a nie 90 - 63 (= 27). I podobnie dla wszystkich innych. –

+1

@ C.M.Sperberg-McQueen: Przy nieco bliższym spojrzeniu znajdziesz ', $ width + 1'. ;-) W komputerze myśli są lepsze obliczenia niż ludzie. –

2

Aby wstawić łańcuch na określoną długość w XSLT 1.0, użyłbym kombinacji concat() i substring(). W matrycę o szczegóły, na przykład, mogę napisać coś podobnego

<xsl:value-of 
    select="substring(concat(SSN,'   '),1,9)"/> 
<xsl:value-of 
    select="substring(concat(DOB,'   '),1,8)"/> 
<xsl:value-of 
    select="substring(concat(LastName,'    '),1,16)"/> 
... 
<xsl:text>&#xA;</xsl:text> 

Jeśli wiesz bardzo niewiele o XSLT, będzie trzeba także nauczyć się konstruować stylów: XSLT zazwyczaj używa szablonu dopasowanie do kierowania przepływem kontroluj w arkuszu stylów, który często jest trudny do zrozumienia dla osób wywodzących się z imperatywnych języków programowania.

Jeśli wiesz, że każdy element szczegółów będzie miał te same elementy podrzędne w tej samej sekwencji (jest to jedna rzecz, dla której są odpowiednie definicje DTD i schematy), najprostszą rzeczą do zrobienia jest napisanie szablonu dla każdego typu elementu, który może występują na wejściu. Poniższy arkusz stylów ilustruje wzór dla niektórych, lecz nie wszystkich elementów:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="1.0"> 

    <xsl:variable name="blanks10" select="   "/> 
    <xsl:variable name="blanks" 
    select="concat($blanks10, $blanks10, $blanks10)"/> 

    <!--* For Report, table1, and Detail_collection, we just 
     * recur on the children *--> 
    <xsl:template match="Report | table1 | Detail_collection"> 
    <xsl:apply-templates select="*"/> 
    </xsl:template> 

    <!--* For Detail, we recur on the children and supply a 
     * line-ending newline. *--> 
    <xsl:template match="Detail"> 
    <xsl:apply-templates select="*"/> 
    <xsl:text>&#xA;</xsl:text> 
    </xsl:template> 

    <!--* For SSN, DOB, etc., we pad the value with blanks and 
     * truncate at the appropriate length. *--> 
    <xsl:template match="SSN"> 
    <xsl:value-of select="substring(concat(.,$blanks),1,9)" 
    </xsl:template> 

    <!--* For DOB, we assume input is yyyy/mm/dd and output should 
     * be yyyymmdd. *--> 
    <xsl:template match="DOB"> 
    <xsl:value-of 
     select="substring(concat(translate(.,'/',''),$blanks),1,8)" 
    </xsl:template> 

    <xsl:template match="LastName"> 
    <xsl:value-of select="substring(concat(.,$blanks),1,16)" 
    </xsl:template>  

    <!--* FirstName etc. left as exercise for the reader. *--> 

</xsl:stylesheet> 

Jeżeli Szczegóły mogą się różnić w kolejności lub populacji, zmiana może być znormalizowane zastępując wezwanie do xsl: apply-templates w szablonie dla szczegółu z kod taki jak tutaj w pierwszym fragmencie kodu. Ten styl kodu jest także bardziej naturalny dla niektórych programistów proceduralnych; z tego powodu radzę unikać tego świadomie podczas nauki XSLT. Jeśli chcesz dobrze się nauczyć XSLT, opłaca się zaprzyjaźnić z xsl: apply-templates.

Jeśli nie dbasz o uczenie się XSLT, to moja rada to mieć nadzieję, że ktoś odpowie na twoje pytanie, dając ci kompletne rozwiązanie twojego zadania.

Powiązane problemy