Added readme and some basic documentation

This commit is contained in:
Mark van Renswoude 2020-12-16 14:55:36 +01:00
parent 98c0901b4c
commit 65b651a489
11 changed files with 574 additions and 0 deletions

3
.gitignore vendored
View File

@ -6,3 +6,6 @@ lib/
__history/
*.stat
Docs/_build
Docs/*.sublime-workspace

20
Docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = X2XMLDataBinding
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

14
Docs/README.md Normal file
View File

@ -0,0 +1,14 @@
The documentation can be built locally using Sphinx. Install Python 3 (choco install python on Windows),
then install sphinx and the ReadTheDocs theme:
```pip install sphinx sphinx_rtd_theme```
To build the HTML output, run:
```.\make.bat html```
To use the auto reloading server (rundev.bat), install the sphinx-autobuild package:
```pip install sphinx-autobuild```

View File

@ -0,0 +1,10 @@
{
"folders":
[
{
"path": ".",
"file_exclude_patterns": ["*.sublime-project"],
"folder_exclude_patterns": ["_build"]
}
]
}

158
Docs/conf.py Normal file
View File

@ -0,0 +1,158 @@
# -*- coding: utf-8 -*-
#
# X²XMLDataBinding documentation build configuration file, created by
# sphinx-quickstart on Sat Feb 11 13:02:50 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'X²XMLDataBinding'
author = u'Mark van Renswoude'
html_show_copyright = False
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u''
# The full version, including alpha/beta/rc tags.
release = u''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'X²XMLDataBindingdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'X²XMLDataBinding.tex', u'X²XMLDataBinding Documentation',
u'Mark van Renswoude', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'X²XMLDataBinding', u'X²XMLDataBinding Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'X²XMLDataBinding', u'X²XMLDataBinding Documentation',
author, 'X²XMLDataBinding', 'Alternative XML data binding generator for Delphi.',
'Miscellaneous'),
]
highlight_language = 'delphi'

276
Docs/hints.rst Normal file
View File

@ -0,0 +1,276 @@
Hints file
==========
Introduction
------------
There are a few cases where the generated output can be influenced by providing a hints file. You can generate a skeleton hints file by opening X²XMLDataBinding, selecting a schema file and clicking the "Generate blank hints file" button. The filename must be :code:`<XSD filename without extension>.hints.xml` to be recognized.
The empty file looks like this:
::
<?xml version="1.0"?>
<DataBindingHints xmlns="http://www.x2software.net/xsd/databinding/DataBindingHints.xsd">
</DataBindingHints>
In the sections below each available hint will be described. All hints will require at least two parameters: a Schema and an XPath expression.
The Schema can either be an empty string or the name of the XSD file (without the .xsd extension) which contains the element you want to modify. If the Schema is empty, the root schema (the one you select as the Schema file in the tool) will be used.
The XPath expression identifies the element *as present in the XSD file* on which the hint will be applied.
Specifying document elements
----------------------------
The data binding will try to determine the document element(s) from the XSD by creating Get/Load/New functions for global elements in the root schema. If the actual element you require is in an included schema, or if you want define a different set of document elements as to not get confused when using the binding, you can use the `DocumentElements` hint to describe those.
For example, this schema with two global elements:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ChildElement">
<xs:complexType>
<xs:sequence>
<xs:element name="Value" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="RootElement">
<xs:complexType>
<xs:sequence>
<xs:element ref="ChildElement"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
This will result in the following functions:
::
{ Document functions }
function GetChildElement(ADocument: XMLIntf.IXMLDocument): IXMLChildElement;
function LoadChildElement(const AFileName: String): IXMLChildElement;
function LoadChildElementFromStream(AStream: TStream): IXMLChildElement;
function LoadChildElementFromString(const AString: String{$IF CompilerVersion >= 20}; AEncoding: TEncoding = nil; AOwnsEncoding: Boolean = True{$IFEND}): IXMLChildElement;
function NewChildElement: IXMLChildElement;
function GetRootElement(ADocument: XMLIntf.IXMLDocument): IXMLRootElement;
function LoadRootElement(const AFileName: String): IXMLRootElement;
function LoadRootElementFromStream(AStream: TStream): IXMLRootElement;
function LoadRootElementFromString(const AString: String{$IF CompilerVersion >= 20}; AEncoding: TEncoding = nil; AOwnsEncoding: Boolean = True{$IFEND}): IXMLRootElement;
function NewRootElement: IXMLRootElement;
To define only the RootElement as a document element, use the following hint:
.. code-block:: xml
<?xml version="1.0"?>
<DataBindingHints xmlns="http://www.x2software.net/xsd/databinding/DataBindingHints.xsd">
<DocumentElements>
<DocumentElement Schema="" XPath="//xs:element[@name='RootElement']" />
</DocumentElements>
</DataBindingHints>
Renaming interfaces
-------------------
The interface names are generated based on various factors, like the name of the complex type or the element, and will be made unique if required by prefixing them with the parent element's name and/or adding a number. This can result in some unexpected names. For example, using this XSD:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Order">
<xs:complexType>
<xs:sequence>
<xs:element name="Info" type="Info"/>
<xs:element name="Customer">
<xs:complexType>
<xs:sequence>
<xs:element name="Info">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
<xs:element name="DateOfBirth" type="xs:date"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Salesperson">
<xs:complexType>
<xs:sequence>
<xs:element name="Info">
<xs:complexType>
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Info">
<xs:sequence>
<xs:element name="OrderNumber" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
When generated, due to the order in which the interfaces are resolved, the interface for Order.Info will be named 'IXMLInfo3', whereas the other two will be aptly named 'IXMLCustomerInfo' and 'IXMLSalespersonInfo'.
This is not an issue if you directly assign the Order.Info property, but if you need a variable or parameter of that type it is not immediately clear from your code which Info element it corresponds to.
To change this you can use an InterfaceName hint:
.. code-block:: xml
<?xml version="1.0"?>
<DataBindingHints xmlns="http://www.x2software.net/xsd/databinding/DataBindingHints.xsd">
<Interfaces>
<InterfaceName Schema="" XPath="//xs:complexType[@name='Info']">OrderInfo</InterfaceName>
</Interfaces>
</DataBindingHints>
This will result in the interface to be named IXMLOrderInfo instead.
Renaming properties
-------------------
Much like renaming interfaces you can also rename properties. This is useful if you don't like the underscore added for reserved words, or if the XML you are trying to parse or write has confusing element names. For example, we had a supplier who 'minified' the XML by naming all elements "V1" through "V185".
Example XSD:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Order">
<xs:complexType>
<xs:sequence>
<xs:element name="V1" type="xs:dateTime"/>
<xs:element name="V2" type="xs:string"/>
<xs:element name="V3" type="xs:string"/>
<xs:element name="V4" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Example hints file:
.. code-block:: xml
<?xml version="1.0"?>
<DataBindingHints xmlns="http://www.x2software.net/xsd/databinding/DataBindingHints.xsd">
<Properties>
<PropertyName Schema="" XPath="//xs:element[@name='V1']">CreationDate</PropertyName>
<PropertyName Schema="" XPath="//xs:element[@name='V2']">CreatedBy</PropertyName>
<PropertyName Schema="" XPath="//xs:element[@name='V3']">Currency</PropertyName>
<PropertyName Schema="" XPath="//xs:element[@name='V4']">Market</PropertyName>
</Properties>
</DataBindingHints>
As you can see from the generated file, the elements will still be read as 'V1', 'V2', etc. but you can use the self-explanatory name of CreationDate to access that element in code.
::
IXMLOrder = interface(IXMLNode)
...
property CreationDate: TDateTime read GetCreationDate write SetCreationDate;
property CreatedBy: WideString read GetCreatedBy write SetCreatedBy;
property Currency: WideString read GetCurrency write SetCurrency;
property Market: WideString read GetMarket write SetMarket;
end;
...
function TXMLOrder.GetCreationDate: TDateTime;
begin
Result := XMLToDateTime(ChildNodes['V1'].NodeValue, xdtDateTime);
end;
function TXMLOrder.GetCreatedBy: WideString;
begin
Result := ChildNodes['V2'].Text;
end;
Renaming enumeration values
---------------------------
There are two main use cases for renaming enumeration values:
#. The element is defined as an enumeration but the values are hard to read in code or even generate invalid results
#. The element is defined as a string by the supplier of the XSD but is an enumeration for all intents and purposes and you want to use it as such in code
This example XSD contains both use cases:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Rule">
<xs:complexType>
<xs:sequence>
<xs:element name="Condition" type="Condition"/>
<xs:element name="Field" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:simpleType name="Condition">
<xs:restriction base="xs:string">
<xs:enumeration value="="/>
<xs:enumeration value="&lt;"/>
<xs:enumeration value="GreaterThan"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
For the 'Condition' the three enum values will all be generated as `Condition_` as the data binding can not handle these special characters, and the result will not even compile.
The 'Field' may be a known set of values which you want to treat as an enum for easy case statements. In this case we can define enumeration hints as well. Note that if the value in an XML is not one of the defined enums, you can still read it's value by using the `FieldText` property which is generated in addition to the typed `Field` property.
.. code-block:: xml
<?xml version="1.0"?>
<DataBindingHints xmlns="http://www.x2software.net/xsd/databinding/DataBindingHints.xsd">
<Enumerations>
<Enumeration Schema="" XPath="//xs:simpleType[@name='Condition']">
<Member Name="=">Condition_Equals</Member>
<Member Name="&lt;">Condition_LessThan</Member>
</Enumeration>
<Enumeration Schema="" XPath="//xs:element[@name='Field']" ReplaceMembers="true">
<Member Name="CustomerName">Field_CustomerName</Member>
<Member Name="TotalAmount">Field_TotalAmount</Member>
</Enumeration>
</Enumerations>
</DataBindingHints>
At the time of writing, renaming enumeration members only works for simple types, which is why it was defined separately in the example above and referenced from the element.
The ReplaceMembers attribute determines if the provided set of Members is to be considered the full new set (true) or a translation of specific members (false). In the example above, if ReplaceMembers was set to true for the Condition, the 'GreaterThan' member would be left out of the generated binding.

9
Docs/index.rst Normal file
View File

@ -0,0 +1,9 @@
X²XMLDataBinding documentation
==============================
.. toctree::
:maxdepth: 2
:caption: Table of contents
introduction
hints

28
Docs/introduction.rst Normal file
View File

@ -0,0 +1,28 @@
Introduction
============
X²XMLDataBinding is an alternative to the Delphi built-in XML data binding generator with a few benefits.
The tool has been compiled and tested with Delphi 10.2, your mileage may vary with other versions. It was originally compiled with Delphi XE2 and probably still works on many versions.
The files it generates are most likely compatible with Delphi XE2 and up as well, perhaps even lower versions, let me know if you have experience with them!
Key features
------------
* Proper namespace support
* Output to a single file or multiple files, allowing types to be reused when included from several root schemas
* Has<Name> properties for optional elements and attributes
* <Name>IsNil properties for xsi:nil elements
* <Name>Def methods to safely read optional values with a default
* Read and write enumeration properties as a typed enum or raw text
* Proper boolean support
* Much improved support for date/time values
* Support for Base64 encoded values
* Basic validation for outputting XML documents which applies element order for sequences and checks for the presence of required elements
* Influence the generator by using a :doc:`hints`
Note: some features might already be present in the built-in XML data binding generator, but have been buggy in older Delphi versions and some still are.
This tool does not implement the full XSD specification, as some parts are obviously a design by committee. However, feel free to enhance the parsing if you have a particular schema which can not be processed!

36
Docs/make.bat Normal file
View File

@ -0,0 +1,36 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=X2XMLDataBinding
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd

3
Docs/rundev.bat Normal file
View File

@ -0,0 +1,3 @@
@Echo Off
start "" "http://localhost:8000/"
sphinx-autobuild . .\_build\html -N

17
README.md Normal file
View File

@ -0,0 +1,17 @@
# X²XMLDataBinding
*an alternative to the Delphi built-in XML data binding generator with a few benefits.*
## Key features
- Proper namespace support
- Output to a single file or multiple files, allowing types to be reused when included from several root schemas
- Has<Name> properties for optional elements and attributes
- <Name>IsNil properties for xsi:nil elements
- <Name>Def methods to safely read optional values with a default
- Read and write enumeration properties as a typed enum or raw text
- Proper boolean support
- Much improved support for date/time values
- Support for Base64 encoded values
- Basic validation for outputting XML documents which applies element order for sequences and checks for the presence of required elements
- Influence the generator by using a Hints file
Documentation is available on [ReadTheDocs](https://x2xmldatabinding.readthedocs.io/).