Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simple-schema suggestions #312

Open
FreHu opened this issue Mar 8, 2021 · 5 comments
Open

simple-schema suggestions #312

FreHu opened this issue Mar 8, 2021 · 5 comments

Comments

@FreHu
Copy link
Contributor

FreHu commented Mar 8, 2021

I am trying to use xml-tools to implement a language on top of xml (it's some kind of crossover of xsd and xslt, but this does not really matter for the purpose of these suggestions).

I believe I can work around these by implementing custom validators, but it would be nice if simple-schema supported

Recursive type definitions

I am using an xml file to describe the structure of an xml file, therefore I need recursive elements, e.g.

<element name="foo">
  <element name="bar">
    <attribute name="attr"><attribute>
  </element>
</element>

is used to represent

<foo>
  <bar attr=""/>
</foo>
const commonNodeProperties: Record<string, XSSAttribute> = {
	name: {
		key: "name",
		required: true
	},
	description: {
		key: "description",
		required: false
	},
	restriction: {
		key: "restriction",
		required: false,
	},
	mappingTarget: {
		key: "mappingTarget",
		required: false,
	},
	mapping: {
		key: "mapping",
		required: false,
	}
};


export const rootElementSchema: XSSElement = {
	name: "element",
	cardinality: "single",
	required: true,
	attributesType: "closed",
	attributes: {
		...commonNodeProperties
	},
	elements: {
		element: {
			name: "element",
			cardinality: "many",
			required: true,
			attributesType: "closed",
			attributes: {
				...commonNodeProperties
			},
			elements: {
                          // element can contain element, attribute with cardinality many
                        },
			elementsType: "open"
		},
		attribute: {
			name: "attribute",
			cardinality: "many",
			required: true,
			attributesType: "closed",
			attributes: {
				...commonNodeProperties
			},
			elements: {},
			elementsType: "open"
		}
	},
	elementsType: "open"
};

export const schema: SimpleSchema = {
	name: "ifme",
	required: true,
	require: true,
	cardinality: "single",
	attributes: {},
	attributesType: "closed",
	elements: {
		environment: environmentSchema,
		constants: constantsSchema,
		enumerations: enumerationsSchema,
		restrictions: restrictionsSchema,
		tree: {
			name: "tree",
			required: true,
			cardinality: "single",
			attributes: {},
			elements: {
				element: rootElementSchema
			},
			attributesType: "closed",
			elementsType: "closed"
		},
	},
};

but I don't think I will be able to accomplish this with simple-schema currently.

Empty attribute value validation

I can specify that an attribute is required, but it is also likely that a required attribute should have a non-empty value.

[minor] XSSElement type definitions

I wish elements and attributes were optional properties, in many cases you don't need them and you have to explicitly use empty objects.

const thatWillBeAllThanks: any = {
	attributes: {},
	elements: {}
};

const environmentSchema: XSSElement = {
	name: "environment",
	required: true,
	cardinality: "single",
	attributes: {
	},
	elements: {
		type: {
			name: "type",
			cardinality: "single",
			required: true,
			...thatWillBeAllThanks
		},
		id: {
			name: "id",
			cardinality: "single",
			required: true,
			...thatWillBeAllThanks
		},
		description: {
			name: "description",
			cardinality: "single",
			required: false,
			...thatWillBeAllThanks
		},
	},
};
@bd82
Copy link
Member

bd82 commented Mar 10, 2021

Hello @FreHu

The simple-schema package was created for a specific use case by @voicis' team/group.
So the willingness to expend it and/or accept contributions in this context needs their input.

@voicis WDYT?

@FreHu
Copy link
Contributor Author

FreHu commented Mar 10, 2021

If simple-schema was created for a specific use case maybe it is not wise for me to depend on.

I started evaluating whether it wouldn't be a wiser choice to base my language on json instead of xml, as then I can use vscode's native json schema validation support instead of relying on simple-schema or some other language server with xsd based validation. A json schema can be generated from typescript definitions, so this would make for a very maintainable solution.

I will be spending some time trying it out. It's possible I won't need simple-schema after all, which makes my suggestions very-very low priority.

@bd82
Copy link
Member

bd82 commented Mar 10, 2021

What exactly are you building?

Are you expending the syntax of XML/JSON or just defining some semantics / Schema in XML/JSON syntax?

@voicis
Copy link
Contributor

voicis commented Mar 10, 2021

Hello @FreHu and @bd82,

Here are my thoughts on the proposals:

  1. Recursive type definitions
    If we want to add recursive type definitions to the simple schema it would have to be done in a generic manner. Getting it right would not be easy and there would still be cases for which it doesn't fit and you would need to resort to custom implementation of validators and content assist. So I would like to avoid adding this to the simple schema right now.

    In our use case we also have recursive structures inside the XML documents, but for those we implement custom validators and content assist. My suggestion for you would be the same when you need to deal with recursive structures.

  2. Empty attribute value validation
    I believe you can already do this by specifying regex for the attribute value.

const schema = {
  name: "person",
  required: true,
  cardinality: "single",
  attributes: {
      name: {
          require: true,
          value: /\w+/
      }
  },
  elements: {
  },
};
  1. XSSElement type definitions
    I think this is a good idea. It would make the schema more concise and easier to use.

@FreHu
Copy link
Contributor Author

FreHu commented Mar 22, 2021

Thanks for the replies, I somehow overlooked the regex option.

I've made a little progress on my project and now I have something I could contribute back if you find it useful:

I didn't like that I have to repeat the name twice

"type": {
   name: "type",
},

so I ended up writing a few helpers

import {XSSElement, XSSAttribute, SimpleSchema} from "@xml-tools/simple-schema";

/** Type without listed properties */
export type Without<T, K extends keyof T>
	= Pick<T, Exclude<keyof T, K>>;

export function schema_root(
	elementName: string,
	rest: Without<XSSElement, "name" | "cardinality">)
	: SimpleSchema {
	return {
		require: true,
		name: elementName,
		cardinality: "single",
		...rest
	};
}

export function single_element(
	elementName: string,
	rest: Without<XSSElement, "name" | "cardinality">)
	: Record<string, XSSElement> {
	return {
		[elementName]: {
			name: elementName,
			cardinality: "single",
			...rest
		}
	};
}

export function many_elements(
	elementName: string,
	rest: Without<XSSElement, "name" | "cardinality">)
	: Record<string, XSSElement> {
	return {
		[elementName]: {
			name: elementName,
			cardinality: "many",
			...rest
		}
	};
}

export function attribute(
	attributeName: string,
	rest: Without<XSSAttribute, "key">): Record<string, XSSAttribute> {
	return {
		[attributeName]: {
			key: attributeName,
			...rest
		}
	};
}

export const required: Pick<XSSElement | XSSAttribute, "required"> = {
	required: true
};

export const optional: Pick<XSSElement | XSSAttribute, "required"> = {
	required: false
};

export const positive_whole_number: Pick<XSSAttribute, "value"> = {
	value: /\d+/
};

export const not_empty: Pick<XSSAttribute, "value"> = {
	value: /\w+/
};

export const boolean: Pick<XSSAttribute, "value"> = {
	value: ["true", "false"]
};

export const no_children: Pick<XSSElement, | "elements" | "elementsType"> = {
	elements: {},
	elementsType: "closed"
};

export const no_attributes: Pick<XSSElement, | "attributes" | "attributesType"> = {
	attributes: {},
	attributesType: "closed"
};

export const only_specified_attributes: Pick<XSSElement, | "attributesType"> = {
	attributesType: "closed"
};

export const only_specified_elements: Pick<XSSElement, | "elementsType"> = {
	elementsType: "closed"
};

Now if we overuse the ... operator a little, the schema becomes more human readable and composable. Type validations and autocompletion are preserved.

export const schema: SimpleSchema =

  schema_root("root", {

    ...required,
    ...only_specified_attributes,
    ...only_specified_elements,
    ...no_children,
  
  attributes: {
    ...mapping_type,
    ...mapping_id,
    ...version,
    ...output_structure_name,
    ...description,
  },

  elements: {
    ...single_element("tree", {
    
	    ...required,
	    ...no_attributes,
	    ...only_specified_elements,
	    ...only_specified_attributes,
    
	    elements: {
              ...constants,
              ...enumerations,
              ...restrictions,
              ...root_element,
	    },
       })
 }
});
export const enumerations = single_element("enumerations", {

  ...optional,
  ...no_attributes,
  
  elements: many_elements("enumeration", {
  
  ...required,
  ...only_specified_attributes,
  ...only_specified_elements,
  
  attributes: {
      ...name,
      ...data_type,
      ...description
  },
  
  elements: {
      ...many_elements("enum", {
      ...required,
      ...no_children,
      ...only_specified_attributes,
      attributes: {
        ...value
      },
   })
  },
})
});

(note only part of the schema is included in the example usage)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants