Generate XML with the xml library

hello,

i need to generate an XML document that start with things like that:

<?xml version="1.0"?>
<VOTABLE version="1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns="http://www.ivoa.net/xml/VOTable/v1.2" xmlns:stc="http://www.ivoa.net/xml/STC/v1.30" >
  <RESOURCE>
    <TABLE name="getOrbUrl generation">
      <DESCRIPTION>Generation from service getOrbUrl</DESCRIPTION>
      <GROUP ref="" ID="PosFrame">
	<FIELDref ref="col2" utype="stc:AstroCoords.Position3D.Value3.C1"/>

i use xml like that:

> (xexpr->string `(VOTABLE ((version "1.2") (xmlns:xsi "http://www.w3.org/2001/XMLSchema-instance"))))
(xexpr->string
 `(VOTABLE
   ((version "1.2") (xmlns:xsi "http://www.w3.org/2001/XMLSchema-instance"))))
"<VOTABLE version=\"1.2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"></VOTABLE>"

but i did not found a way in the doc to generate the first line:

<?xml version="1.0"?>

is there another way to do it with xml lib ? better than a single display like that:

;; create output XML file
	(define out (open-output-file #:exists 'truncate data-out))
	(display "<?xml version=\"1.0\"?>" out)
	(newline out)

regards,

damien

Hi, @damien_mattei.

Just a shot in the dark here, but looking at write-plist from the xml lib, I see this:

(define (write-plist xexpr port)
  (write-xml 
   (make-document (make-prolog (list (make-p-i #f #f 'xml "version=\"1.0\" encoding=\"UTF-8\"")) 
                               (make-document-type 'plist
                                                   (make-external-dtd/system
                                                    "http://www.apple.com/DTDs/PropertyList-1.0.dtd")
                                                   #f)
                               empty)
                  (xexpr->xml `(plist ((version "0.9"))
                                      ,(expand-value xexpr)))
                  null)
   port))

So maybe make-document is the way? I hope that is helpful.


Edit: I think the prolog might actually be the important part? I am ignorant apart from reading this: XML syntax.

1 Like

it is interesting but for now i'm still searching to make the xml document with that.

I can not find it in the last version of doc , perheaps is it deprecated,
the example of the actual doc is different.

when i try to test the other example i have this error:

(base) mattei@device-97 src % racket
Welcome to Racket v8.13 [cs].
> (require xml)
> (define xexpr `(VOTABLE ((version "1.2") (xmlns:xsi "http://www.w3.org/2001/XMLSchema-instance"))))
> (make-document (make-prolog (list (make-p-i #f #f 'xml "version=\"1.0\" encoding=\"UTF-8\"")) 
                                 (make-document-type 'plist
                                                     (make-external-dtd/system
                                                      "http://www.apple.com/DTDs/PropertyList-1.0.dtd")
                                                     #f)
                                 empty)
                    (xexpr->xml `(plist ((version "0.9"))
                                        ,(expand-value xexpr)))
                    null)
expand-value: undefined;
 cannot reference an identifier before its definition
  in module: top-level
 [,bt for context]
> (xexpr->xml `(plist ((version "0.9"))
                                          ,(expand-value xexpr)))
expand-value: undefined;
 cannot reference an identifier before its definition
  in module: top-level
 [,bt for context]

i cannot find reference to expand-value in the current Racket doc.

@bakgatviooldoos thank you it works when removing the undefined expand-value:

(write-xml
 (make-document
  (make-prolog
   (list (make-p-i #f #f 'xml "version=\"1.0\" encoding=\"UTF-8\""))
   (make-document-type
    'plist
    (make-external-dtd/system "http://www.apple.com/DTDs/PropertyList-1.0.dtd")
    #f)
   empty)
  (xexpr->xml `(plist ((version "0.9")) ,xexpr))
  null))

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="0.9"><VOTABLE version="1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></VOTABLE></plist>#<eof>
1 Like

oh i did not understand it was not an example but probably the source code of write-plist...

Haha, I apologize, I did not mean to pass this off as mine own. I should have linked to the code, but I was simply looking at the files in my editor.

I am glad you have made progress, though.

i just post the code as example of creating XML with xml library and using Schme+ for Racket , not so often i can use racket at work and i used too Scheme+ for this : given a data text file the goal was to generate an XML file, the input data looks like this :

# BepiColombo Mio Orbit after insertion
# Extracted from Sae Aizawa in ~ 2021 
# One sample per minute
# Modified by F. Lavorenti in Oct. 2022
# index		X [Rm]		Y [Rm]		Z [Rm]
0.000000	-5.226162	-0.133805	1.462782
1.000000	-5.239695	-0.133871	1.446325
2.000000	-5.253049	-0.133932	1.429818
3.000000	-5.266226	-0.133988	1.413263
4.000000	-5.279225	-0.134038	1.396661
5.000000	-5.292047	-0.134083	1.380013
6.000000	-5.304692	-0.134123	1.363318
7.000000	-5.317161	-0.134158	1.346578

...

1428.000000	-0.730541	0.029664	-2.397660
1429.000000	-0.680719	0.028417	-2.378984
1430.000000	-0.630639	0.027154	-2.359445
1431.000000	-0.580337	0.025879	-2.339026
1432.000000	-0.529815	0.024591	-2.317697
1433.000000	-0.479085	0.023290	-2.295427
1434.000000	-0.428158	0.021976	-2.272187
1435.000000	-0.377049	0.020649	-2.247945
1436.000000	-0.325759	0.019309	-2.222663
1437.000000	-0.274306	0.017956	-2.196307
1438.000000	-0.222710	0.016590	-2.168841
1439.000000	-0.170992	0.015211	-2.140229

1446 lines in BepiColombo-Mio_MSO-orbit_1min_short.txt
consisting of BepiColombo spacecraft trajectory, the position is known at each minute (index) and X,Y,Z are expressed in Mercure radius and will be converted in Km.

here is the code of generateXMLfromTrajectory :

#! /usr/bin/env racket
#lang reader SRFI-105

;; parse the Bepi Colombo spacecraft trajectory file text and convert it in XML format

;; Damien MATTEI

;; ./generateXMLfromTrajectory ./BepiColombo-Mio_MSO-orbit_1min_short.txt

(module generateXMLfromTrajectory racket
	
	(require Scheme+)
	
	(require xml
		 (except-in 2htdp/batch-io xexpr?)) ; for: read-lines
	
	;;(require (only-in racket/base [for for-racket]))
	
	(require srfi/19) ; Time Data Types and Procedures

	;; parse the input file from command line
	(define cmd-ln-vect (current-command-line-arguments))

	(define Rm 2439.7) ; Rayon Mercure (km)

	{src <- cmd-ln-vect[0]}
	(display "Input file:")
	(display src)
	(newline)

	;; check we have a .txt file
	{ext <- src[-4 :]} ; try to get the .txt extension
	(display "Extension find:")
	(display ext)
	(newline)

	(when (not (equal? ext ".txt"))
	      (error "Not a text file."))

	;; read all lines
	{input-lines <- (read-lines src)}
	{vl <- (list->vector input-lines)}

	;; find the basename
	{basename <- src[: -4]} ; skip the 4 char of extension

	;; create output file name
	{data-out <- (string-append "../" basename ".xml")}

	;; create output XML file
	(define out (open-output-file #:exists 'truncate data-out))

	{tdl <- vl[5 :]}; TABLEDATA lines, skip the header to go to table data lines

	; convert the index (minute) in days hours minutes
	(define (minutes->days-hours-minutes m) ; minutes
	  {minutes-in-day <- 60 * 24}
	  (define days (quotient m minutes-in-day))
	  {minutes <- m % minutes-in-day} ; partial resting minutes
	  (define hours (quotient minutes 60))
	  {minutes <- minutes % 60} ; final resting minutes
	  (values days hours minutes))

	;; my routine to convert date time in a string using SRFI 19 Time Data Types and Procedures
	(define (my-time->string d h m)
	  (define dt19 (make-date 0 0 m h d 1 2021 0))
	  (string-append (date->string dt19 "~5") ".000")) ; append nanoseconds
   
	
	(define (parse-data-table-lines tdl) ; get a vector of lines

	  (for/list ([tr tdl])
		    
		    {(index-minutes xs ys zs) <- (apply values (string-split tr))}
		    
		    ;; convert from Rm mercury in km
		    (define x (string->number xs))
		    (define y (string->number ys))
		    (define z (string->number zs))
		    {x <- x * Rm}
		    {y <- y * Rm}
		    {z <- z * Rm}
		    {xs <- (number->string x)}
		    {ys <- (number->string y)}
		    {zs <- (number->string z)}
		    
		    {(d h m) <- (minutes->days-hours-minutes (inexact->exact (round (string->number index-minutes))))}
		    
		    (define dt-str (my-time->string {d + 1} h m))
		    
		    `(TR (TD ,dt-str)
			  (TD ,xs)
			  (TD ,ys)
			  (TD ,zs))))

	
	
	(define xexpr `(VOTABLE ((version "1.2")
				 (xmlns:xsi "http://www.w3.org/2001/XMLSchema-instance")
				 (xmlns:stc "http://www.ivoa.net/xml/STC/v1.30"))
				(RESOURCE
				 (TABLE ((name "getOrbUrl generation"))
					(DESCRIPTION ,{string-append(vl[0] vl[1] vl[2] vl[3])})
					(GROUP ((ref "")
						(ID "PosFrame"))
					       (FIELDref ((ref "col2")
							  (utype "stc:AstroCoords.Position3D.Value3.C1")))
					       (FIELDref ((ref "col3")
							  (utype "stc:AstroCoords.Position3D.Value3.C2")))
					       (FIELDref ((ref "col4")
							  (utype "stc:AstroCoords.Position3D.Value3.C3"))))
					(FIELD ((name "Time")
						(ID "col1")
						(ucd "time.epoch")
						(xtype "dateTime")
						(utype "")
						(datatype "char")
						(arraysize "*")))
					(FIELD ((name "X")
						(ID "col2")
						(ucd "pos.cartesian.x")
						(utype "stc:AstroCoords.Position3D.Value3.C1")
						(datatype "float")
						(width "10") (unit "km")))
					(FIELD ((name "Y")
						(ID "col3")
						(ucd "pos.cartesian.y")
						(utype "stc:AstroCoords.Position3D.Value3.C2")
						(datatype "float")
						(width "10") (unit "km")))
					(FIELD ((name "Z")
						(ID "col4")
						(ucd "pos.cartesian.z")
						(utype "stc:AstroCoords.Position3D.Value3.C3")
						(datatype "float")
						(width "10") (unit "km")))
					(DATA
					 (TABLEDATA ,@(parse-data-table-lines tdl)))))))



	;; this is the main code
	
	(display-xml
	 (make-document
	  (make-prolog
	   ;;(list (make-p-i #f #f 'xml "version=\"1.0\" encoding=\"UTF-8\""))
	   (list (make-p-i #f #f 'xml "version=\"1.0\""))
	   ;; (make-document-type
	   ;;  'plist
	   ;;  (make-external-dtd/system "http://www.apple.com/DTDs/PropertyList-1.0.dtd")
	   ;;  #f)
	   #f
	   empty)
	  ;;(xexpr->xml `(plist ((version "0.9")) ,xexpr))
	  (xexpr->xml xexpr)
	  null)
	 out)
	  

	
) ; end module

and here is a part of the output XML file generated (BepiColombo-Mio_MSO-orbit_1min_short.xml) :

<?xml version="1.0"?>

<VOTABLE version="1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:stc="http://www.ivoa.net/xml/STC/v1.30">
  <RESOURCE>
    <TABLE name="getOrbUrl generation">
      <DESCRIPTION>
        # BepiColombo Mio Orbit after insertion# Extracted from Sae Aizawa in ~ 2021 # One sample per minute# Modified by F. Lavorenti in Oct. 2022
      </DESCRIPTION>
      <GROUP ref="" ID="PosFrame">
        <FIELDref ref="col2" utype="stc:AstroCoords.Position3D.Value3.C1">
        </FIELDref>
        <FIELDref ref="col3" utype="stc:AstroCoords.Position3D.Value3.C2">
        </FIELDref>
        <FIELDref ref="col4" utype="stc:AstroCoords.Position3D.Value3.C3">
        </FIELDref>
      </GROUP>
      <FIELD name="Time" ID="col1" ucd="time.epoch" xtype="dateTime" utype="" datatype="char" arraysize="*">
      </FIELD>
      <FIELD name="X" ID="col2" ucd="pos.cartesian.x" utype="stc:AstroCoords.Position3D.Value3.C1" datatype="float" width="10" unit="km">
      </FIELD>
      <FIELD name="Y" ID="col3" ucd="pos.cartesian.y" utype="stc:AstroCoords.Position3D.Value3.C2" datatype="float" width="10" unit="km">
      </FIELD>
      <FIELD name="Z" ID="col4" ucd="pos.cartesian.z" utype="stc:AstroCoords.Position3D.Value3.C3" datatype="float" width="10" unit="km">
      </FIELD>
      <DATA>
        <TABLEDATA>
          <TR>
            <TD>
              2021-01-01T00:00:00.000
            </TD>
            <TD>
              -12750.2674314
            </TD>
            <TD>
              -326.4440585
            </TD>
            <TD>
              3568.7492454
            </TD>
          </TR>
          <TR>
            <TD>
              2021-01-01T00:01:00.000
            </TD>
            <TD>
              -12783.2838915
            </TD>
            <TD>
              -326.6050787
            </TD>
            <TD>
              3528.5991025
            </TD>
          </TR>
          <TR>
            <TD>
              2021-01-01T00:02:00.000
            </TD>
            <TD>
              -12815.863645299998
            </TD>
            <TD>
              -326.75390039999996
            </TD>
            <TD>
              3488.3269745999996
            </TD>
          </TR>
          <TR>
            <TD>
              2021-01-01T00:03:00.000
            </TD>
            <TD>
              -12848.011572199997
            </TD>
            <TD>
              -326.89052359999994
            </TD>
            <TD>
              3447.9377410999996
            </TD>
          </TR>


...




         <TR>
            <TD>
              2021-01-01T23:57:00.000
            </TD>
            <TD>
              -669.2243481999999
            </TD>
            <TD>
              43.8072532
            </TD>
            <TD>
              -5358.3301879
            </TD>
          </TR>
          <TR>
            <TD>
              2021-01-01T23:58:00.000
            </TD>
            <TD>
              -543.3455869999999
            </TD>
            <TD>
              40.474623
            </TD>
            <TD>
              -5291.3213877
            </TD>
          </TR>
          <TR>
            <TD>
              2021-01-01T23:59:00.000
            </TD>
            <TD>
              -417.16918239999995
            </TD>
            <TD>
              37.1102767
            </TD>
            <TD>
              -5221.5166913
            </TD>
          </TR>
        </TABLEDATA>
      </DATA>
    </TABLE>
  </RESOURCE>
</VOTABLE>

+1 for using Racket at work! :partying_face:

1 Like