PDA

View Full Version : XML and FG



Shrp77
August 24th, 2006, 11:25
I've noticed a lot of people talking about having "skill with XML" to modify/create rulesets for FG. Being a fairly anal person (for those who think gutter here: it's an expression for people who are detail oriented) I've started to get a twitch every time I read that people would like to help by being good with XML.

XML isn't more than a way of writing something. Think of it as a strongly enforced HTML document. In fact, Fantasy Grounds doesn't use 100% valid XML syntaxing, most prominently shown in the FormattedText nodes which embeds HTML code inside the XML node (which is a nono since HTML uses XML-like syntaxes which cause xml validators to go a little nuts).

XML can basically be boiled down to this: Every element MUST have a closing syntax (unlike HTML, where the closing syntax is not required, but rather injected by the layout engine. Of course, this can cause some rather amusing errors in the page - but that's another topic all-together).

An element can be closed in two ways; single line termination or element termination.

Example:



Single line termination
<MyXMLNode />

where:
"<" starts the node and "/>" ends the element

Element termination
<MyXMLNode>
</MyXMLNode>

Note that when using element termination, the terminating character "/" comes in the beginning of the node, not at the end (just like HTML).


The attributes used in an XML element typically comes as a key-value pair, i.e. that every attribute has a name that identifies the attribute and a value.



<StringValue name="Something">

where "name" identifies the attribute and "Something" is the value assigned to that attribute.


In order to create or modify FG rulesets you really don't need to be strong in XML, but you need to understand the datastructures that the XML code represent - and that's where the magic happens.

The big questions are: How to the elements and attributes link together. What combinations are legal to FG and what combinations will cause the program to blow up?

If possible, I think it would be beneficial for XSD's (XML Schema Document -- basically a blueprint as to how an XML file is assembled)to be posted here, which would indicate the makeup of the various files.

I'll upload the XSD's I've generated and am using for my FaGrEd application.

If we can fill out the schemas with relevant information, then we'd be a long way towards having a "working document" on how to write the different modules for FG! :ninja:

Shrp77
August 24th, 2006, 11:26
This is the XSD for the BASE.XML file:



<xs:schema id="root">
<xs:element name="root">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="staticdata">
<xs:complexType>
<xs:attribute name="source" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="includefile">
<xs:complexType>
<xs:attribute name="source" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="desktopimage">
<xs:complexType>
<xs:attribute name="file" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>


Now - what do the entries actually mean?

first off - ignore the "xs:" tag. It signifies to XML validation tools and code generators that this is a schema file and not a data file. I.e. you can safely ignore it when trying to see the construction of an XML file.

element
This represents a node in the file. The name value is the name of the node. Example of nodes are:



<root> <- Node
<staticdata source="myfile.xml" /> <- Node with attribute

which could look like this:
<xs:element name="root">
<xs:element name="staticdata">
<xs:attribute name="source" type="xs:string" />
</xs:element>
</xs:element>

or

<xs:element name="root">
<xs:complexType>
<xs:element name="staticdata">
<xs:complexType>
<xs:attribute name="source" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:complexType>
</xs:element>


attribute
This represents a key-value pair within an element. It tells you the name of the attribute and the data type stored in the attribute. Some attributes may only allow numbers in them, which would be a form of "integer" but you are most likely going to encounter "string" data types, which means text.

complexType
This tells you that the entries under here consist of a variation of entities. If multiple elements are defined under a complexType it means that you may encounter any of those elements in varying numbers.

Automatic schema assemblers have a tendency to overload the output with complexTypes in order to allow for flexible XML documents to be created based on such a scheme.

choice
This regulates the amount of entities you may encounter of underlying data in a complexType. The attributes for Choise are: minOccurs and maxOccurs. They respectively tell you the least amount of times you can expect to find any element and the maximum number of times you can find an element.

The value "unbounded" means that there is no limit.

Shrp77
August 24th, 2006, 11:54
Here's the XSD for the CharSheet Part 1 (darned 10000 character limit):



s:schema id="root">
<xs:element name="bounds">
<xs:complexType>
<xs:attribute name="rect" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="description">
<xs:complexType>
<xs:attribute name="text" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="tabtarget">
<xs:complexType>
<xs:attribute name="next" type="xs:string" />
<xs:attribute name="prev" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="font">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="class">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="root">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="bounds" />
<xs:element ref="description" />
<xs:element ref="tabtarget" />
<xs:element ref="font" />
<xs:element ref="class" />
<xs:element name="windowclass">
<xs:complexType>
<xs:sequence>
<xs:element name="minimize" type="xs:string" minOccurs="0" msdata:Ordinal="1" />
<xs:element name="nodelete" type="xs:string" minOccurs="0" msdata:Ordinal="2" />
<xs:element name="playercontrol" type="xs:string" minOccurs="0" msdata:Ordinal="3" />
<xs:element name="frame" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="datasource" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="dynamic" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="resize" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="defaultsize" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="width" type="xs:string" />
<xs:attribute name="height" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="defaultposition" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="x" type="xs:string" />
<xs:attribute name="y" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="sheetdata" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="numbercontrol" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="nodrag" type="xs:string" minOccurs="0" msdata:Ordinal="1" />
<xs:element name="nodrop" type="xs:string" minOccurs="0" msdata:Ordinal="2" />
<xs:element name="noreset" type="xs:string" minOccurs="0" msdata:Ordinal="3" />
<xs:element name="center" type="xs:string" minOccurs="0" msdata:Ordinal="4" />
<xs:element ref="bounds" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="dropoperation" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="op" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element ref="description" minOccurs="0" maxOccurs="unbounded" />
<xs:element ref="tabtarget" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="source" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
<xs:attribute name="op" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element ref="font" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="hideonvalue" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="value" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="stringcontrol" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="center" type="xs:string" minOccurs="0" msdata:Ordinal="1" />
<xs:element name="static" type="xs:string" minOccurs="0" msdata:Ordinal="2" />
<xs:element ref="bounds" minOccurs="0" maxOccurs="unbounded" />
<xs:element ref="tabtarget" minOccurs="0" maxOccurs="unbounded" />
<xs:element ref="description" minOccurs="0" maxOccurs="unbounded" />
<xs:element ref="font" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="multiline" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="spacing" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="windowopencontrol" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element ref="bounds" minOccurs="0" maxOccurs="unbounded" />
<xs:element ref="class" minOccurs="0" maxOccurs="unbounded" />
<xs:element ref="description" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="subwindow" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="activate" type="xs:string" minOccurs="0" msdata:Ordinal="1" />
<xs:element ref="class" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>

Shrp77
August 24th, 2006, 11:55
CharSheet part 2:



<xs:element name="windowchangecontrol" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element ref="bounds" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="target" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="windowlist" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="allowcreate" type="xs:string" minOccurs="0" msdata:Ordinal="1" />
<xs:element name="allowdelete" type="xs:string" minOccurs="0" msdata:Ordinal="2" />
<xs:element name="nestdata" type="xs:string" minOccurs="0" msdata:Ordinal="3" />
<xs:element ref="bounds" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="b-ounds" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="rect" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="sort" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="fields" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="filter" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="field" type="xs:string" />
<xs:attribute name="value" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="class" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>



This introduces some new types and values:

element ref
If you encounter a line that reads: <xs:element ref="xyz" /> then you need to go find the line that reads <xs:element name="xyz" />.

Basically it's a way to keep the XSD down in size. Instead of repeating the description for an element again and again, you can create the definition once, and simply refer to the existing definition later on.

element ... msdata:Ordinal="X"
The ordinal value is basically used to make sure elements or other data are listed in a specific sequence.

Ordinal sequences are always listed from 0 and up.

You will always find the Ordinal value in conjunction with the sequence node.

sequence
This tells you that the data under it is sequential and is listed in a specific order. You will typically find an Ordinal attribute in the elements under it, however this is not required.

I do not believe Fantasy Grounds uses sequencing in the parsing of the XML files, however it's nice to know that the XML data typically follows a set pattern...

richvalle
August 24th, 2006, 14:13
Nice! If I ever plan on playing with the xml I'll have to go back and read this. :)

rv

tdwyer11b
August 24th, 2006, 18:31
Thanx for posting the schema's, now if I can figure out how to get em to work, hehe. I agree with you though, you really don't need to be a guru when it comes to working with FG XML. Just a little courage and a lot of patience.:) The most important thing I've found is to do a little at a time (makes it easier to track down sections of the code your changing that FG doesn't like)

Shrp77
August 24th, 2006, 22:53
This schema covers the files in the database folder.



<xs:schema id="root">
<xs:element name="node">
<xs:complexType>
<xs:sequence>
<xs:element name="stringvalue">
<xs:complexType>
<xs:attribute name="name" />
<xs:attribute name="value" />
<xs:attribute name="node_Id" />
</xs:complexType>
</xs:element>
<xs:element name="intvalue" />
<xs:complexType>
<xs:attribute name="name" />
<xs:attribute name="value" />
<xs:attribute name="node_Id" />
</xs:complexType>
</xs:element>
<xs:element name="formattedtext" />
<xs:complexType>
<xs:attribute name="name" />
<xs:attribute name="node_Id" />
</xs:complexType>
</xs:element>
<xs:element ref="node" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="name" />
<xs:attribute name="node_Id" />
<xs:attribute name="node_Id_0" />
</xs:complexType>
</xs:element>
</xs:schema>


NOTE: the formattedtext node must be read as literal text! The contents of this node is HTML formatted, so if you are making your own serializer you must implement it as a CDATA type (if using C# you can implement the member as inherited from IXmlSerializable and implement the ReadXml function using the ReadInnerXml function).

sloejack
August 27th, 2006, 23:12
This may seem like a silly question but how are you applying the schema to the files? I've looked into using a WYSIWYG XML editor to see if it would be easier to insert data into modules and rulesets but most seem to balk at the fact that the xml doesn't have the appropriate headers and stuff.

Shrp77
August 28th, 2006, 06:48
This may seem like a silly question but how are you applying the schema to the files? I've looked into using a WYSIWYG XML editor to see if it would be easier to insert data into modules and rulesets but most seem to balk at the fact that the xml doesn't have the appropriate headers and stuff.

I'm not using the schemas to implement the XML files directly (although that is an option). As you pointed out - the xml editors balk at the code because it isn't true XML (as you said, the header is missing, among other things).

I use the schemas to create data objects which can serialize (i.e. write) and deserialize (i.e. read) the xml files into C# classes. That way I don't need to mess around with creating the read/write code as it's done for me by the data layer.

The purpose of the schema files is to see the options that you can use for a specific node (i.e. the data structure).

If the XML program supports applying schemas or templates, you could use the code below (you would have to insert the XML header, which looks like this:



<?xml version="1.0" encoding="utf-8"?>


The encoding is optional).

The XML header will be generated by any XML creator, however you can simply edit the file afterwards and remove it (seeing as FG doesn't use it... I don't know if the header would be ignored by FG or if it would be parsed and crash the program...).

sloejack
August 28th, 2006, 06:58
For what it is worth, FG seems to ignore any tags it doesn't recognize. I understand what you mean about using the schema to build an appropriate parser/generator. I think I was hoping that I could cheat the system a little bit and use something like XMLSpy or something similar to be able to generate structure and be able to just plug in data without having to either do it all by hand, build a parser/generator, or wait for someone else to release a parser/generator.

Thanks for posting the schemas and for your reply.

JemearlS
August 30th, 2006, 16:27
I wanted to give public thanks to Shrp77. He took time out of his day to give me some pointers on how to serialize/deserialize XML into C# classes.

Thanks Shrp77, you're an asset to the FG community!

Shrp77
August 31st, 2006, 07:37
You're quite welcome. The more hands we have on writing cool stuff for FG, the bigger the benefit for the community (even if we do end up with 15 different character sheet editors etc.)