Wygenerowanie xmla na podstawie wzoru

0

cześć

Potrzebuję wygenerować plik xml na podstawie wzoru, który również jest xml'em. Przykład:

wzór:

 <xml>
  <head>
      <eye></eye>
  </head>
  <body>
      <stomach></stomach>
      <chest></chest>
  </body>
  <name></name>
  <surname></surname>
</xml>

zadany xml:

  <xml>
  <head>
      <eye>blue</eye>
      <nose>long</nose>
      <teeth>healthy</teeth>
  </head>
  <body>
      <stomach>fat</stomach>
      <chest>hairy</chest>
      <nipples>pink</nipples>
      <arms>short</arms>
  </body>
  <name>John</name>
  <surname>Smith</surname>
 <address>London</address>
</xml>

Oczekiwany wynik:

  <xml>
  <head>
      <eye>blue</eye>
  </head>
  <body>
      <stomach>fat</stomach>
      <chest>hairy</chest>
  </body>
  <name>John</name>
  <surname>Smith</surname>
</xml>

Robie to tak: czytam cały wzór po kolei i dla każdego node'a tworze wskazania w postaci stringa. Następnie za pomocą tych wskazań wyciągam określone node'y z zadanego xmla. Przetrzymuje je w liście XmlNode i dalej mam problem z załadowaniem ich do nowego dokumentu. Zastanawiam się czy obrane podejście jest dobre. Dajcie znać proszę, czy macie jakieś inne pomysły rozwiązania problemu, lub konkretne bilbioteki. Ja korzystam z obiektów XmlDocument, XmlNode itp, jednak znalazłem jeszcze XDocument, XNode itp. Dodatkowo jest jeszcze LINQ to XML i sie gubie w tym wszystkim. Albo jestem bardzo blisko, albo musze zrobićwszystko od nowa za pomocą innej "technologii". Nie oczukuję gotowego kodu, tylko podpowiedzi jakiegoś innego workflow w tym programie.

0

Jak to ma być elastyczne, w sensie czy wzór jest jeden i kilka plików do przerobienia, czy dostajesz wzór i drugi plik masz doprowodzaić do zadanej postaci?

0

oczywiście druga opcja

0

To jest dla mnie nie do napisania z automatu, struktura xmla jest na tyle elastyczna, że mając dwa dokumenty nie do końca wiem jak ma wyglądać jeden wspólny, jeszcze nie mając DTD/XSD

Przykład:
Wzór:

<farby>
    <typ><typ>
    <kolory>
        <kolor></kolor>
   </kolory>
</farby>

zadany xml:

<farby>
    <typ>plakatowa<typ>
    <kolory>
        <kolor>zielony</kolor>
        <kolor>czerwony</kolor>        
        <kolor>niebieski</kolor>
   </kolory>
</farby>

I jaka powinna być postać wynikowa?

Żeby nie było, ze marudzę, ja bym szedł w stronę XSLT, powiedzmy że założenie jest takie, uzupełniam wartości węzłów które nie mają dzieci, to robię szablon xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/">
		<xsl:element name = "xsl:stylesheet" namespace = "http://www.w3.org/1999/XSL/Transform" >
			<xsl:attribute name="version">1.0</xsl:attribute>
			<xsl:element name = "xsl:output" namespace = "http://www.w3.org/1999/XSL/Transform" >
				<xsl:attribute name="method">xml</xsl:attribute>
				<xsl:attribute name="indent">yes</xsl:attribute>
			</xsl:element>
			<xsl:element name = "xsl:template" namespace = "http://www.w3.org/1999/XSL/Transform" >
				<xsl:attribute name="match">/</xsl:attribute>
				<xsl:apply-templates select="@* | node()"/>
			</xsl:element>
		</xsl:element>
	</xsl:template>
	<xsl:template match="@* | node()">
		<xsl:copy>
			<xsl:apply-templates select="@* | node()"/>
		</xsl:copy>
	</xsl:template>
	<xsl:template match="*[not(*)]">
		<xsl:element name = "{name(.)}"  >
			<xsl:element name = "xsl:value-of"  >
				<xsl:attribute name="select">
					<xsl:call-template name="genPath"/>
				</xsl:attribute>
			</xsl:element>
		</xsl:element>
	</xsl:template>
	<xsl:template name="genPath">
		<xsl:param name="prevPath"/>
		<xsl:variable name="currPath" select="concat('/',name(),'[', count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/>
		<xsl:for-each select="parent::*">
			<xsl:call-template name="genPath">
				<xsl:with-param name="prevPath" select="$currPath"/>
			</xsl:call-template>
		</xsl:for-each>
		<xsl:if test="not(parent::*)">
			<xsl:value-of select="$currPath"/>
		</xsl:if>
	</xsl:template>
</xsl:stylesheet>

I używam go do transformacji wzoru, co da mi w wyniku:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="/">
      <xml>
         <head>
            <eye>
               <xsl:value-of select="/xml[1]/head[1]/eye[1]"/>
            </eye>
         </head>
         <body>
            <stomach>
               <xsl:value-of select="/xml[1]/body[1]/stomach[1]"/>
            </stomach>
            <chest>
               <xsl:value-of select="/xml[1]/body[1]/chest[1]"/>
            </chest>
         </body>
         <name>
            <xsl:value-of select="/xml[1]/name[1]"/>
         </name>
         <surname>
            <xsl:value-of select="/xml[1]/surname[1]"/>
         </surname>
      </xml>
   </xsl:template>
</xsl:stylesheet>

i tego szablonu używam do transformacji zadanego xml-a

0

Dzięki za obszerną odpowiedź. Podany przez Ciebie przykład xml'i nie spełnia przypadków, które bede rozwazal, a konkretniej to zaden node nie bedzie nigdy mial child'ów o takiej samej nazwie (kolor). Pokaze na innym przykładzie:

input:

 <farby>
   <farba1>
       <cechy>
          <typ>emalia</typ>
          <twardosc>niska</twardosc>
          <polysk>wysoki</polysk>
       </cechy>
       <cena>100</cena>
       <nazwa>
           <skrocona>slonce</skrocona>
           <pelna>Zachod Slonca w Hiszpanii</pelna>
       </nazwa>
   </farba1>
</farby>

wzór:

  <farby>
   <farba1>
       <cechy>
          <typ></typ>
       </cechy>
       <cena></cena>
       <nazwa>
           <skrocona></skrocona>
       </nazwa>
   </farba1>
</farby>

output:

   <farby>
   <farba1>
       <cechy>
          <typ>emalia</typ>
       </cechy>
       <cena>100</cena>
       <nazwa>
           <skrocona>slonce</skrocona>
       </nazwa>
   </farba1>
</farby>

Chodzi o to, ze wynikowy xml posiada dokładnie taką samą strukturę jak wzór ale dodatkowo ma uzupełnione wartości z odpowiednich (relatywnych) node'ow z wejsciowego xmla.

Jesli chodzi o DTD to jest tyle: <?xml version="1.0" encoding="UTF-8"?>

Ja dalej brnę w rozwiązanie, w którym czytam cały wzór i na tej podstawie buduję XPath'a wskazującego na każdy node. Następnie z wejsciowego xmla wyciągam wszystkie node'y za pomocą utworzonych wczesniej XPath'ów. Obecnie mam problemy z tym, ze wyciąga mi np. wszystkie childy jakiegos node'a podczas gdy ja chcę np. tylko jednego określonego. Po wyciagnieciu wszyskich pożądanych node'ow z wejsciowego xmla, zapiszę je do nowego pliku xml.

0

No ale to się sprawdzi w twoim przypadku

W kilku krokach:
#1 PRZYGOTOWANIE DOKUMENTU XML DLA ZADANEGO XML-A
wzorzec XML

<farby>
   <farba1>
       <cechy>
          <typ></typ>
       </cechy>
       <cena></cena>
       <nazwa>
           <skrocona></skrocona>
       </nazwa>
   </farba1>
</farby>

XSLT dla wzorca:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <xsl:element name = "xsl:stylesheet" namespace = "http://www.w3.org/1999/XSL/Transform" >
            <xsl:attribute name="version">1.0</xsl:attribute>
            <xsl:element name = "xsl:output" namespace = "http://www.w3.org/1999/XSL/Transform" >
                <xsl:attribute name="method">xml</xsl:attribute>
                <xsl:attribute name="indent">yes</xsl:attribute>
            </xsl:element>
            <xsl:element name = "xsl:template" namespace = "http://www.w3.org/1999/XSL/Transform" >
                <xsl:attribute name="match">/</xsl:attribute>
                <xsl:apply-templates select="@* | node()"/>
            </xsl:element>
        </xsl:element>
    </xsl:template>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[not(*)]">
        <xsl:element name = "{name(.)}"  >
            <xsl:element name = "xsl:value-of"  >
                <xsl:attribute name="select">
                    <xsl:call-template name="genPath"/>
                </xsl:attribute>
            </xsl:element>
        </xsl:element>
    </xsl:template>
    <xsl:template name="genPath">
        <xsl:param name="prevPath"/>
        <xsl:variable name="currPath" select="concat('/',name(),'[', count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/>
        <xsl:for-each select="parent::*">
            <xsl:call-template name="genPath">
                <xsl:with-param name="prevPath" select="$currPath"/>
            </xsl:call-template>
        </xsl:for-each>
        <xsl:if test="not(parent::*)">
            <xsl:value-of select="$currPath"/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Wynik transformacji docelowy xslt

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/">
    <farby>
   <farba1>
       <cechy>
          <typ><xsl:value-of select="/farby[1]/farba1[1]/cechy[1]/typ[1]"/></typ>
       </cechy>
       <cena><xsl:value-of select="/farby[1]/farba1[1]/cena[1]"/></cena>
       <nazwa>
           <skrocona><xsl:value-of select="/farby[1]/farba1[1]/nazwa[1]/skrocona[1]"/></skrocona>
       </nazwa>
   </farba1>
</farby>
  </xsl:template>
</xsl:stylesheet>

#2 Przetwarzanie zadanego xml-a

Zadany xml transformuje wynikiem z #1 i uzyskuje:

<farby>
  <farba1>
    <cechy>
      <typ>emalia</typ>
    </cechy>
    <cena>100</cena>
    <nazwa>
      <skrocona>slonce</skrocona>
    </nazwa>
  </farba1>
</farby>

**3# wykorzystanie w programie **

Stały jest tylko arkusz xsl dla pliku wzorca, reszta to dynamiczne pliki:
W pseudo kodzie C#
załóżmy, że
wzorzec to plik wzorzec.xml
arkusz xsl dla wzorca wzorzec.xsl
zadany xml to input.xml
https://support.microsoft.com/pl-pl/kb/307322

 
            XslTransform myXslTransform; 
            myXslTransform = new XslTransform();
//przygotuj xsl na podstawie wzorca
            myXslTransform.Load("wzorzec.xsl"); 
            myXslTransform.Transform("wzorzec.xml", "input.xsl"); 

//przetworz input
            myXslTransform.Load("input.xsl"); 
            myXslTransform.Transform("input.xml", "output.xml"); 

0

Dzięki za gotowe rozwiązanie, ale problem jest taki, że ja kompletnie nie znam sie na xslt. Spróbuje zastosować to rozwiązanie u siebie i dam znać :)

0

Jak się przekształca xml-e to warto poznać coś co do tego służy, niż wymyślać koło na nowo...

1 użytkowników online, w tym zalogowanych: 0, gości: 1