<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://battery.knowledge-graph.eu/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=0000-0003-0410-3616</id>
	<title>Battery Knowledge Base - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://battery.knowledge-graph.eu/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=0000-0003-0410-3616"/>
	<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/wiki/Special:Contributions/0000-0003-0410-3616"/>
	<updated>2026-04-07T04:23:39Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.5</generator>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWf4b1c9e38ac74d3fb1a79a2f6c5b2d3e&amp;diff=18550</id>
		<title>Item:OSWf4b1c9e38ac74d3fb1a79a2f6c5b2d3e</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWf4b1c9e38ac74d3fb1a79a2f6c5b2d3e&amp;diff=18550"/>
		<updated>2026-01-30T02:34:45Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWf4b1c9e38ac74d3fb1a79a2f6c5b2d3e&amp;diff=18549</id>
		<title>Item:OSWf4b1c9e38ac74d3fb1a79a2f6c5b2d3e</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWf4b1c9e38ac74d3fb1a79a2f6c5b2d3e&amp;diff=18549"/>
		<updated>2026-01-30T02:32:28Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c&amp;diff=18546</id>
		<title>Item:OSWaeffcee25ccb5dd8b42a434dc644d62c</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c&amp;diff=18546"/>
		<updated>2026-01-08T16:28:11Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: add personal README from github&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:Viewer/Github/Code&lt;br /&gt;
| url = https://github.com/simontaurus/simontaurus/edit/main/README.md&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c&amp;diff=18545</id>
		<title>Item:OSWaeffcee25ccb5dd8b42a434dc644d62c</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c&amp;diff=18545"/>
		<updated>2026-01-08T16:23:20Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Biography==&lt;br /&gt;
Simon Stier received his Bachelor&#039;s degree in Aerospace Informatics and Technology of Functional Materials from JMU Würzburg in 2014 and his subsequent Master&#039;s degrees in Computer Science and Functional Materials from JMU Würzburg in 2017. In 2021, he completed his PhD in the field of interdisciplinary research on materials, processes and sensor applications at the Graduate School for Science and Technology Würzburg. He is currently Head of the Department for Digital Transformation at Fraunhofer ISC Würzburg and member of the European Materials Modelling Council (EMMC) and the Batteries European Partnership Association (BEPA) Taskforce Digitalization.&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW662db0a2ad0946148422245f84e82f64&amp;diff=18330</id>
		<title>Category:OSW662db0a2ad0946148422245f84e82f64</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW662db0a2ad0946148422245f84e82f64&amp;diff=18330"/>
		<updated>2025-03-19T17:54:36Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasDescription=Description&lt;br /&gt;
|?HasIri=IRI&lt;br /&gt;
|format=datatable&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWc131224eceb6481d869300a4ee749610&amp;diff=18321</id>
		<title>Item:OSWc131224eceb6481d869300a4ee749610</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSWc131224eceb6481d869300a4ee749610&amp;diff=18321"/>
		<updated>2025-03-07T13:03:26Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW2d384748a6dc45e58f36ce57fd2dfbff&amp;diff=18315</id>
		<title>Item:OSW2d384748a6dc45e58f36ce57fd2dfbff</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW2d384748a6dc45e58f36ce57fd2dfbff&amp;diff=18315"/>
		<updated>2025-03-07T12:07:16Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: update list&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to the Battery Knowledge Base Development Roadmap page, where we present an in-depth exploration of current open topics and future plans in the development of this dynamic resource. &lt;br /&gt;
&lt;br /&gt;
As the field of battery technology continues to evolve at an unprecedented pace, this knowledge base aims to stay at the forefront of innovation by addressing key areas of research, industry needs, and technological challenges. Current open topics include the exploration of new battery chemistries, such as solid-state and lithium-sulfur technologies, advancements in energy density, efficiency, and the environmental impacts of battery production and recycling. The Knowledge Base also delves into emerging trends in battery applications, including electric vehicles (EVs), grid-scale energy storage, and portable electronics. Another critical area of focus is the ongoing development of safety standards and regulations, driven by the growing complexity and diversity of battery systems. &lt;br /&gt;
&lt;br /&gt;
Looking ahead, the future plans for this platform involve expanding coverage on breakthrough innovations like quantum batteries and next-generation supercapacitors, as well as fostering collaborative efforts with researchers, policymakers, and industry leaders to enhance sustainable battery manufacturing processes. Through continuous updates, expert contributions, and collaborative projects, the Battery Knowledge Base seeks to become a pivotal resource for professionals, academics, and enthusiasts looking to navigate the future of energy storage technology.&lt;br /&gt;
{| class=&amp;quot;wikitable sortable mw-collapsible&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Name&lt;br /&gt;
!Description&lt;br /&gt;
!Responsible&lt;br /&gt;
!Date Opened&lt;br /&gt;
!Priority, High = 1, Low = 3&lt;br /&gt;
!Type&lt;br /&gt;
!Status&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;s&amp;gt;Finalize Tiles on the start page&amp;lt;/s&amp;gt;&lt;br /&gt;
|add icons, button to view list of existing entities, tile for characterisation methods, battery cells, standard operating procedures, other common things. Should we distinguish between research organizations and companies?&lt;br /&gt;
=&amp;gt; Done, except battery cells, standard operating procedures. To we have categories/lists here?&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-27&lt;br /&gt;
|1&lt;br /&gt;
|Feature&lt;br /&gt;
|Closed&lt;br /&gt;
|-&lt;br /&gt;
|Enable match on abbrevations in the search&lt;br /&gt;
|For example, EIS should return Electrochemical Impedance Spectroscopy&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-27&lt;br /&gt;
|3&lt;br /&gt;
|Feature&lt;br /&gt;
|Open&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;s&amp;gt;Fix chameo ontology term links&amp;lt;/s&amp;gt;&lt;br /&gt;
|the correct namespace is [https://w3id.org/emmo/domain/characterisation-methodology/chameo https://w3id.org/emmo/domain/characterisation-methodology/chameo#]&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-27&lt;br /&gt;
|2&lt;br /&gt;
|Bug&lt;br /&gt;
|Closed&lt;br /&gt;
|-&lt;br /&gt;
|Create descriptions / article content from LLM (ISC can only support here)&lt;br /&gt;
|May use AI form completion &amp;amp; article writing tools&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSW72e733f317ef4cf9b8ca53e906c9acb9|url=|label=Simon Clark}}, Philipp Dechent,  {{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-27&lt;br /&gt;
|2&lt;br /&gt;
|Feature&lt;br /&gt;
|Open&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;s&amp;gt;Provide a ChatGPT plugin&amp;lt;/s&amp;gt;&lt;br /&gt;
|Prototype: Chat function in search bar, form completion, article writing support&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-27&lt;br /&gt;
|2&lt;br /&gt;
|Feature&lt;br /&gt;
|Closed&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;s&amp;gt;Add term for Event Series&amp;lt;/s&amp;gt;&lt;br /&gt;
|Event Series is referenced in the Event schema, but doesn&#039;t seem to be an actual category. A category was created: [[:Category:OSW2fcf163e903a4c63b45b96c7785970ff|EventSeries - Battery Knowledge Base (knowledge-graph.eu)]]&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-27&lt;br /&gt;
|2&lt;br /&gt;
|Bug&lt;br /&gt;
|Closed&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;s&amp;gt;Import BattINFO Manually&amp;lt;/s&amp;gt;&lt;br /&gt;
|Import the latest version of BattINFO by hand&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSW72e733f317ef4cf9b8ca53e906c9acb9|url=|label=Simon Clark}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-28&lt;br /&gt;
|1&lt;br /&gt;
|Feature&lt;br /&gt;
|Closed&lt;br /&gt;
|-&lt;br /&gt;
|Expand core developers group to support ISC&lt;br /&gt;
|&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSW72e733f317ef4cf9b8ca53e906c9acb9|url=|label=Simon Clark}},  {{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}&lt;br /&gt;
|2024-08-28&lt;br /&gt;
|1&lt;br /&gt;
|Admin&lt;br /&gt;
|Open&lt;br /&gt;
|-&lt;br /&gt;
|Improve page auto-layout&lt;br /&gt;
|The scaling of content is sometimes affected by the knowledge panel on the right. For example, an article like this that includes a table needs a long paragraph of text that exceeds the length of the knowledge panel in order to display the table correctly.&lt;br /&gt;
Fix: use width=100%&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}, {{Template:Viewer/Link|page=Item:OSW72e733f317ef4cf9b8ca53e906c9acb9|url=|label=Simon Clark}}&lt;br /&gt;
|2024-08-28&lt;br /&gt;
|2&lt;br /&gt;
|Bug&lt;br /&gt;
|In work&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;s&amp;gt;Editing homepage throws an error&amp;lt;/s&amp;gt;&lt;br /&gt;
|[64cea9ca95bc42f7c5b21d9e] Caught exception of type Error. This is due to the Navigation graph (the flow chart). It can be commented out to activated the graphical editor.&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-28&lt;br /&gt;
|1&lt;br /&gt;
|Bug&lt;br /&gt;
|Closed&lt;br /&gt;
|-&lt;br /&gt;
|Properties don&#039;t display in Create Wizard&lt;br /&gt;
|Some properties that are selected in the Create wizard do not actually display on the form. For example, you can select IRI from the list, but it does not provide a field to enter it.&lt;br /&gt;
=&amp;gt; Those properties are hidden by purpose (should not be edited by the user). However, it is true that the property menu should not display them in the first place&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}&lt;br /&gt;
|2024-08-28&lt;br /&gt;
|1&lt;br /&gt;
|Bug&lt;br /&gt;
|Open&lt;br /&gt;
|-&lt;br /&gt;
|Rework Organization category &lt;br /&gt;
|Make the organization a proper replacement for the ResearchOrganization tile on the main page --&amp;gt; allow Subcategories only for the &amp;quot;type&amp;quot; property&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=}}&lt;br /&gt;
|2024-08-28&lt;br /&gt;
|2&lt;br /&gt;
|Enhancement&lt;br /&gt;
|Open&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;s&amp;gt;Update Logo&amp;lt;/s&amp;gt;&lt;br /&gt;
|Change the default OSL logo to a battery icon&lt;br /&gt;
=&amp;gt; Just the UTF-Battery Icon or a more specific one?&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=}}, {{Template:Viewer/Link|page=Item:OSW72e733f317ef4cf9b8ca53e906c9acb9|url=|label=Simon Clark}}&lt;br /&gt;
|2024-08-29&lt;br /&gt;
|1&lt;br /&gt;
|Enhancement&lt;br /&gt;
|Closed&lt;br /&gt;
|-&lt;br /&gt;
|Improve SEO&lt;br /&gt;
|This Knowledge Base should be easy to find via google search&lt;br /&gt;
=&amp;gt; It helps to create many links (from Github, institute pages, social media etc) to the Knowledge Base. A SE friendly sitemap is also generated. Improving Schema.org annotations and embedd jsonld into pages should also help &lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}, {{Template:Viewer/Link|page=Item:OSW72e733f317ef4cf9b8ca53e906c9acb9|url=|label=Simon Clark}}&lt;br /&gt;
|2024-08-29&lt;br /&gt;
|1&lt;br /&gt;
|Enhancement&lt;br /&gt;
|Open&lt;br /&gt;
|-&lt;br /&gt;
|Import standard resource for geographical entities&lt;br /&gt;
|We should import something like GeoNames or geographical entities from Wikidata to avoid that people have to keep re-creating entities like cities and countries&lt;br /&gt;
|{{Template:Viewer/Link|page=Item:OSWaeffcee25ccb5dd8b42a434dc644d62c|url=|label=Simon Stier}}, {{Template:Viewer/Link|page=Item:OSW8dca6aaebe005c5faca05bac33264e4d|url=|label=Lukas Gold}}, {{Template:Viewer/Link|page=Item:OSW72e733f317ef4cf9b8ca53e906c9acb9|url=|label=Simon Clark}}&lt;br /&gt;
|2024-09-06&lt;br /&gt;
|1&lt;br /&gt;
|Enhancement&lt;br /&gt;
|Open&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Kanban Board ===&lt;br /&gt;
{{Editor/Kanban&lt;br /&gt;
| file_name = kanban-01&lt;br /&gt;
| full_width = 1&lt;br /&gt;
| edit = 1&lt;br /&gt;
| uuid = 8e71d117-cac5-4036-8aa1-109549c904d9&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW970ef4c4b724478196e21710421645ca&amp;diff=15536</id>
		<title>Item:OSW970ef4c4b724478196e21710421645ca</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW970ef4c4b724478196e21710421645ca&amp;diff=15536"/>
		<updated>2025-03-01T05:45:19Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW30054d8c178e47cc9ec58b5c0accb4f2&amp;diff=15482</id>
		<title>Item:OSW30054d8c178e47cc9ec58b5c0accb4f2</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW30054d8c178e47cc9ec58b5c0accb4f2&amp;diff=15482"/>
		<updated>2025-03-01T05:44:28Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW424bf7507df549b5ba73ba74397a166b&amp;diff=13568</id>
		<title>Category:OSW424bf7507df549b5ba73ba74397a166b</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW424bf7507df549b5ba73ba74397a166b&amp;diff=13568"/>
		<updated>2025-03-01T05:14:34Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: import from https://osl-sandbox.big-map.eu/wiki/Category:OSW424bf7507df549b5ba73ba74397a166b?veaction=editsource&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Zinc electrodes&#039;&#039;&#039; are electrochemical components used in various battery technologies, electroplating, and corrosion protection systems. Due to its high electrochemical activity, low cost, and environmental compatibility, zinc is widely utilized in [[primary battery|primary]] and  [[secondary battery|secondary batteries]], as well as in sacrificial anodes for corrosion prevention.&lt;br /&gt;
&lt;br /&gt;
==Electrochemical Properties==&lt;br /&gt;
Zinc is a relatively reactive metal with a standard electrode potential of &#039;&#039;&#039;−0.76 V vs. SHE (Standard Hydrogen Electrode)&#039;&#039;&#039;. It readily oxidizes to form zinc ions (&#039;&#039;&#039;Zn²⁺&#039;&#039;&#039;), making it an effective anode material in galvanic cells. The redox reaction of zinc in aqueous electrolytes is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\text{Zn} \rightarrow \text{Zn}^{2+} + 2e^-&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This high electrochemical activity allows zinc to function efficiently in electrochemical applications, particularly in &#039;&#039;&#039;alkaline&#039;&#039;&#039;, &#039;&#039;&#039;acidic&#039;&#039;&#039;, and &#039;&#039;&#039;neutral aqueous electrolytes&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Applications==&lt;br /&gt;
&lt;br /&gt;
===Batteries===&lt;br /&gt;
Zinc electrodes are extensively used in both [[primary battery|primary (non-rechargeable)]] and [[secondary battery|secondary (rechargeable)]] batteries:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;[[Zinc–carbon battery|Zinc-carbon batteries]] :&#039;&#039;&#039; The earliest commercial dry cells use a &#039;&#039;&#039;zinc anode&#039;&#039;&#039; with a {{Template:Viewer/Link|page=Category:OSWdcdbdbed2e2040d1a7a55761de7f0618|url=|label=}}  (&#039;&#039;&#039;MnO₂&#039;&#039;&#039;) cathode and an acidic electrolyte (ammonium chloride or zinc chloride).&lt;br /&gt;
*&#039;&#039;&#039;[[Alkaline battery|Alkaline batteries]] :&#039;&#039;&#039; Employ a zinc powder anode with a &#039;&#039;&#039;potassium hydroxide (KOH) electrolyte&#039;&#039;&#039;, offering improved energy density and shelf life over zinc-carbon cells.&lt;br /&gt;
*{{Template:Viewer/Link|page=Category:OSWe8eada7338114bbe8f65f6ee089d439f|url=|label=}}&#039;&#039;&#039;:&#039;&#039;&#039; Utilize atmospheric oxygen as the cathode reactant, making them lightweight and energy-dense. These are used in [[hearing aid|hearing aids]] and other low-power applications.&lt;br /&gt;
*&#039;&#039;&#039;[[Zinc-ion battery|Zinc-ion batteries]]:&#039;&#039;&#039; A developing rechargeable battery technology that uses a &#039;&#039;&#039;zinc metal anode&#039;&#039;&#039; and &#039;&#039;&#039;intercalating cathodes&#039;&#039;&#039;, offering a safer and more sustainable alternative to  [[Lithium-ion battery|lithium-ion batteries]].&lt;br /&gt;
&lt;br /&gt;
===Electroplating===&lt;br /&gt;
Zinc electrodes are used in {{Template:Viewer/Link|page=Category:OSWa2b92d2e4431411e8da5a4c08bac2c0e|url=|label=}} processes to provide a &#039;&#039;&#039;protective coating&#039;&#039;&#039; on steel and other metals. This &#039;&#039;&#039;zinc coating&#039;&#039;&#039; ({{Template:Viewer/Link|page=Category:OSW16d1606ce56243cda92c0894abc2027b|url=|label=}}) prevents corrosion by acting as a &#039;&#039;&#039;sacrificial anode&#039;&#039;&#039;, corroding preferentially to the underlying metal.&lt;br /&gt;
&lt;br /&gt;
===Sacrificial Anodes===&lt;br /&gt;
Zinc electrodes serve as &#039;&#039;&#039;sacrificial anodes&#039;&#039;&#039; in corrosion protection systems for:&lt;br /&gt;
*[[Ships]] and [[marine engineering|marine structures]] (e.g., hulls, propellers, offshore platforms)&lt;br /&gt;
*[[Pipeline transport|Pipelines]] and underground tanks&lt;br /&gt;
*[[Water heater|Water heaters]] and cooling systems&lt;br /&gt;
&lt;br /&gt;
In these applications, zinc prevents corrosion by preferentially oxidizing instead of the protected metal.&lt;br /&gt;
&lt;br /&gt;
===Electrowinning and Electrorefining===&lt;br /&gt;
Zinc electrodes are used in the &#039;&#039;&#039;electrochemical extraction (electrowinning)&#039;&#039;&#039; and &#039;&#039;&#039;purification (electrorefining)&#039;&#039;&#039; of zinc from ores and recycled materials.&lt;br /&gt;
&lt;br /&gt;
==Advantages and Challenges==&lt;br /&gt;
&lt;br /&gt;
===Advantages===&lt;br /&gt;
*&#039;&#039;&#039;Abundant and inexpensive:&#039;&#039;&#039; Zinc is widely available and low-cost.&lt;br /&gt;
*&#039;&#039;&#039;High energy density:&#039;&#039;&#039; Compared to other metal electrodes, zinc provides a high theoretical capacity.&lt;br /&gt;
*&#039;&#039;&#039;Environmental safety:&#039;&#039;&#039; Unlike lead or cadmium, zinc is less toxic and more environmentally friendly.&lt;br /&gt;
*&#039;&#039;&#039;Rechargeability potential:&#039;&#039;&#039; Emerging zinc-based rechargeable batteries offer promising alternatives to lithium-ion.&lt;br /&gt;
&lt;br /&gt;
===Challenges===&lt;br /&gt;
*&#039;&#039;&#039;Dendrite formation:&#039;&#039;&#039; During repeated charging, zinc metal can form {{Template:Viewer/Link|page=Category:OSW34bc54e3de4c41f88d3ad77c951ee23a|url=|label=}}s, leading to short circuits in rechargeable zinc batteries.&lt;br /&gt;
*&#039;&#039;&#039;Corrosion in aqueous systems:&#039;&#039;&#039; Zinc readily reacts with water, leading to self-discharge and inefficiency in certain battery chemistries.&lt;br /&gt;
*&#039;&#039;&#039;Limited cycle life in rechargeable systems:&#039;&#039;&#039; Rechargeable zinc batteries suffer from capacity degradation over time due to &#039;&#039;&#039;zinc shape change&#039;&#039;&#039; and &#039;&#039;&#039;side reactions&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Recent Developments==&lt;br /&gt;
Research in &#039;&#039;&#039;zinc-based batteries&#039;&#039;&#039; focuses on:&lt;br /&gt;
*&#039;&#039;&#039;Dendrite suppression techniques&#039;&#039;&#039;, such as electrolyte additives and solid-state electrolytes.&lt;br /&gt;
*&#039;&#039;&#039;Alternative zinc cathodes&#039;&#039;&#039; for high-performance rechargeable batteries.&lt;br /&gt;
*&#039;&#039;&#039;Hybrid zinc-ion chemistries&#039;&#039;&#039; that improve lifespan and efficiency.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
*[[Zinc–air battery]]&lt;br /&gt;
*[[Zinc–carbon battery]]&lt;br /&gt;
*[[Zinc-ion battery]]&lt;br /&gt;
*[[Galvanization]]&lt;br /&gt;
*[[Electrochemical series]]&lt;br /&gt;
*[[Sacrificial anode]]&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
*&#039;&#039;Electrochemical Energy Storage for Renewable Systems&#039;&#039; – Smith, J. et al.&lt;br /&gt;
*&#039;&#039;Advances in Zinc-Based Battery Technologies&#039;&#039; – Liu, Y. et al.&lt;br /&gt;
*&#039;&#039;Handbook of Corrosion Science and Engineering&#039;&#039; – Jones, D. A.&lt;br /&gt;
&lt;br /&gt;
[[Category:Electrodes]]&lt;br /&gt;
[[Category:Zinc]]&lt;br /&gt;
[[Category:Batteries]]&lt;br /&gt;
[[Category:Electrochemistry]]&lt;br /&gt;
[[Category:Corrosion prevention]]&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW0e084decca6f48a7b023d6b7b2c1452d&amp;diff=13567</id>
		<title>Category:OSW0e084decca6f48a7b023d6b7b2c1452d</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW0e084decca6f48a7b023d6b7b2c1452d&amp;diff=13567"/>
		<updated>2025-02-28T10:52:07Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Reverted edits by 0000-0003-0410-3616 (talk) to last revision by 0000-0002-8758-6109&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The events page serves as a central hub for conferences, workshops, webinars, and other gatherings related to battery research. By documenting past and upcoming events, this page helps researchers, industry professionals, and stakeholders stay informed about key discussions, networking opportunities, and advancements in the field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;pagebot-button&amp;quot; data-config=&#039;{&amp;quot;params&amp;quot;: {&amp;quot;categories&amp;quot;: [&amp;quot;{{FULLPAGENAME}}&amp;quot;]}, &amp;quot;label&amp;quot;: &amp;quot;{{#switch: {{USERLANGUAGECODE}} |de=Neues Event erstellen|#default=Add Event}} ➕&amp;quot; }&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Timeline ==&lt;br /&gt;
{{#ask:&lt;br /&gt;
[[{{FULLPAGENAME}}]]  &lt;br /&gt;
 |?HasStartDateAndTime&lt;br /&gt;
 |sort=HasStartDateAndTime&lt;br /&gt;
 |order=descending&lt;br /&gt;
 |format=timeline&lt;br /&gt;
 |timelinebands=MONTH,YEAR&lt;br /&gt;
 |timelineposition=end&lt;br /&gt;
 |limit=1000&lt;br /&gt;
&amp;lt;!-- All options: See https://www.semantic-mediawiki.org/wiki/Help:timeline_format --&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=={{#switch: {{USERLANGUAGECODE}} |de=Kalender|#default=Calendar}}==&lt;br /&gt;
{{#ask:&lt;br /&gt;
[[{{FULLPAGENAME}}]]          &lt;br /&gt;
 |?Display_title_of=title&lt;br /&gt;
 |?HasDescription=description|+lang={{USERLANGUAGECODE}}&lt;br /&gt;
 |?HasStartDateAndTime=Has event start&lt;br /&gt;
 |?HasEndDateAndTime=Has event end                &lt;br /&gt;
&amp;lt;!--  |?Has event icon=icon              &lt;br /&gt;
 |?Has event color=color --&amp;gt;&lt;br /&gt;
 |?HasLocation=location          &lt;br /&gt;
 |format=eventcalendar            &lt;br /&gt;
 |defaultview=month&lt;br /&gt;
 |limit=1000&lt;br /&gt;
&amp;lt;!-- All options: See https://www.semantic-mediawiki.org/wiki/Help:Eventcalendar_format --&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=={{#switch: {{USERLANGUAGECODE}} |de=Liste|#default=List}}==&lt;br /&gt;
{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasStartDateAndTime={{#switch: {{USERLANGUAGECODE}} |en=Start date|de=Startdatum|#default=Start date}}&lt;br /&gt;
|?HasEndDateAndTime={{#switch: {{USERLANGUAGECODE}} |en=End date|de=Enddatum|#default=End date}}&lt;br /&gt;
|format=datatable&lt;br /&gt;
|limit=1000&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWb2d7e6a2eff94c82b7f1f2699d5b0ee3&amp;diff=13566</id>
		<title>Category:OSWb2d7e6a2eff94c82b7f1f2699d5b0ee3</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWb2d7e6a2eff94c82b7f1f2699d5b0ee3&amp;diff=13566"/>
		<updated>2025-02-28T10:51:29Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Project pages provide a structured way to document and share information about research projects within the Battery Knowledge Graph. These pages serve as central hubs for projects related to battery research. By creating a project page, researchers and collaborators can make their work more visible, facilitate knowledge sharing, and connect with others working on similar topics.&amp;lt;div class=&amp;quot;pagebot-button&amp;quot; data-config=&#039;{&amp;quot;params&amp;quot;: {&amp;quot;categories&amp;quot;: [&amp;quot;{{FULLPAGENAME}}&amp;quot;]}, &amp;quot;label&amp;quot;: &amp;quot;{{#switch: {{USERLANGUAGECODE}} |de=Neues Projekt erstellen|#default=Add new project}} ➕&amp;quot; }&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=={{#switch:{{USERLANGUAGECODE}}|en=Gantt Chart|de=Gantt Diagramm|#default=Gantt Chart}}==&lt;br /&gt;
{{#ask:&lt;br /&gt;
 [[{{FULLPAGENAME}}]]&lt;br /&gt;
 |?Display title of=task&lt;br /&gt;
 |?Issue Status=status&lt;br /&gt;
 |?HasStartDate=startdate&lt;br /&gt;
 |?HasEndDate=enddate&lt;br /&gt;
 |?Related To Epic=section&lt;br /&gt;
 |?Issue Priority=priority&lt;br /&gt;
 |format=gantt&lt;br /&gt;
 |axisformat=%d/%m/%Y&lt;br /&gt;
 |diagramtitle=Projects&lt;br /&gt;
 |statusmapping=2 (in progress)=&amp;gt;active;3 (completed)=&amp;gt;done&lt;br /&gt;
 |prioritymapping=1 (high)=&amp;gt;crit; 2 (normal)=&amp;gt;crit&lt;br /&gt;
 |sortkey=title&lt;br /&gt;
 |leftpadding=120&lt;br /&gt;
 |titletopmargin=25&lt;br /&gt;
 |bargap=5&lt;br /&gt;
 |barheight=25&lt;br /&gt;
 |theme=neutral&lt;br /&gt;
 |limit=1000&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=={{#switch:{{USERLANGUAGECODE}}|en=Overview Table|de=Übersichtstabelle|#default=Overview Table}}==&lt;br /&gt;
{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasDescription={{#switch:{{USERLANGUAGECODE}}|en=Description|de=Beschreibung|#default=Description}} |+lang={{USERLANGUAGECODE}}&lt;br /&gt;
|?HasStartDate={{#switch:{{USERLANGUAGECODE}}|en=Start|de=Beginn|#default=Start}}&lt;br /&gt;
|?HasEndDate={{#switch:{{USERLANGUAGECODE}}|en=End|de=Ende|#default=End}}&lt;br /&gt;
|format=datatable&lt;br /&gt;
|limit=1000&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW1969007d5acf40539642877659a02c23&amp;diff=13565</id>
		<title>Category:OSW1969007d5acf40539642877659a02c23</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW1969007d5acf40539642877659a02c23&amp;diff=13565"/>
		<updated>2025-02-28T10:50:11Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Instances ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;pagebot-button&amp;quot; data-config=&#039;{&amp;quot;params&amp;quot;: {&amp;quot;categories&amp;quot;: [&amp;quot;{{FULLPAGENAME}}&amp;quot;]}, &amp;quot;label&amp;quot;: &amp;quot;{{#switch: {{USERLANGUAGECODE}} |de=Neuen Eintrag erstellen|#default=Add Entry}} ➕&amp;quot; }&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasName={{#switch: {{USERLANGUAGECODE}} |de=Kurzname |#default=Short name}}&lt;br /&gt;
|?HasType={{#switch: {{USERLANGUAGECODE}} |de=Typ |#default=Type}} &lt;br /&gt;
|?HasNumberOfMembers={{#switch:{{USERLANGUAGECODE}} |en=Number of members|de=Mitarbeitende (Anzahl) |#default=Number of members }}&lt;br /&gt;
|format=datatable&lt;br /&gt;
|limit=5000&lt;br /&gt;
|sort=Display title of&lt;br /&gt;
|order=asc&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWd9aa0bca9b0040d8af6f5c091bf9eec7&amp;diff=13564</id>
		<title>Category:OSWd9aa0bca9b0040d8af6f5c091bf9eec7</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWd9aa0bca9b0040d8af6f5c091bf9eec7&amp;diff=13564"/>
		<updated>2025-02-28T10:49:30Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Reverted edits by 0000-0003-0410-3616 (talk) to last revision by 0000-0002-8758-6109&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;User pages provide a space for contributors to introduce themselves, share their research interests, and engage with the Battery Knowledge Graph community!&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
These pages serve as a way to connect with other researchers, document contributions, and highlight expertise. Each user page is unique to the individual and can be used to provide information about professional background, ongoing projects, publications, or areas of interest. By maintaining an up-to-date user page, contributors can increase visibility within the community and facilitate collaborations.&lt;br /&gt;
&lt;br /&gt;
== Instances ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;pagebot-button&amp;quot; data-config=&#039;{&amp;quot;params&amp;quot;: {&amp;quot;categories&amp;quot;: [&amp;quot;{{FULLPAGENAME}}&amp;quot;]}, &amp;quot;label&amp;quot;: &amp;quot;{{#switch: {{USERLANGUAGECODE}} |de=Neuen Eintrag erstellen|#default=Add Entry}} ➕&amp;quot; }&#039;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
    |?HasOrganization={{#switch: {{USERLANGUAGECODE}} |en=Organization |de=Organisation |#default=Organization}}&lt;br /&gt;
    |?HasOrganization.HasName={{#switch: {{USERLANGUAGECODE}} |en=Org. Shortname |de=Org. Kurzname |#default=Org. Shortname}}&lt;br /&gt;
    |?HasOu={{#switch: {{USERLANGUAGECODE}} |en=OU |de=OE |#default=OU}}&lt;br /&gt;
    |?-HasMember={{#switch: {{USERLANGUAGECODE}} |en=Member of |de=Mitglied von |#default=Member of }}&lt;br /&gt;
    |format=datatable&lt;br /&gt;
    |limit=1000&lt;br /&gt;
    |sort=Display title of&lt;br /&gt;
    |order=asc&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW970ef4c4b724478196e21710421645ca&amp;diff=13563</id>
		<title>Item:OSW970ef4c4b724478196e21710421645ca</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW970ef4c4b724478196e21710421645ca&amp;diff=13563"/>
		<updated>2025-02-27T11:39:50Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW970ef4c4b724478196e21710421645ca&amp;diff=13562</id>
		<title>Item:OSW970ef4c4b724478196e21710421645ca</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW970ef4c4b724478196e21710421645ca&amp;diff=13562"/>
		<updated>2025-02-27T11:38:41Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW970ef4c4b724478196e21710421645ca&amp;diff=13561</id>
		<title>Item:OSW970ef4c4b724478196e21710421645ca</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW970ef4c4b724478196e21710421645ca&amp;diff=13561"/>
		<updated>2025-02-27T11:33:33Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW5f6f1e51583f472a8fb7426536315174&amp;diff=8919</id>
		<title>Category:OSW5f6f1e51583f472a8fb7426536315174</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW5f6f1e51583f472a8fb7426536315174&amp;diff=8919"/>
		<updated>2025-02-26T18:12:23Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: raname, add missing titles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW3684c31643ea491b90ea885db9ae864e&amp;diff=8918</id>
		<title>Category:OSW3684c31643ea491b90ea885db9ae864e</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW3684c31643ea491b90ea885db9ae864e&amp;diff=8918"/>
		<updated>2025-02-26T18:11:08Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: rename&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW7b6be98f59934e0f900b005395abf981&amp;diff=8917</id>
		<title>Category:OSW7b6be98f59934e0f900b005395abf981</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW7b6be98f59934e0f900b005395abf981&amp;diff=8917"/>
		<updated>2025-02-26T18:01:03Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: rename, add missing titles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWd4b4c5bdb4c54bae815f1a11b62288d3&amp;diff=8916</id>
		<title>Category:OSWd4b4c5bdb4c54bae815f1a11b62288d3</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWd4b4c5bdb4c54bae815f1a11b62288d3&amp;diff=8916"/>
		<updated>2025-02-26T18:00:54Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: rename&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWc15b622daa514022a801849c6593cbdf&amp;diff=8915</id>
		<title>Category:OSWc15b622daa514022a801849c6593cbdf</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWc15b622daa514022a801849c6593cbdf&amp;diff=8915"/>
		<updated>2025-02-26T17:44:54Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: rename, add missing titles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWf50ba030bbb743b196e1739fb67d6862&amp;diff=8914</id>
		<title>Category:OSWf50ba030bbb743b196e1739fb67d6862</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWf50ba030bbb743b196e1739fb67d6862&amp;diff=8914"/>
		<updated>2025-02-26T17:44:45Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: rename&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWdc0857e150454dd3a1e45ce54e9b2903&amp;diff=8913</id>
		<title>Category:OSWdc0857e150454dd3a1e45ce54e9b2903</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWdc0857e150454dd3a1e45ce54e9b2903&amp;diff=8913"/>
		<updated>2025-02-26T17:32:17Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: rename, add missing titles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWae9cf54c182245149a65b0149eaa7c0c&amp;diff=8912</id>
		<title>Category:OSWae9cf54c182245149a65b0149eaa7c0c</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWae9cf54c182245149a65b0149eaa7c0c&amp;diff=8912"/>
		<updated>2025-02-26T17:31:35Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: rename, add missing titles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Instances ==&lt;br /&gt;
{{#ask: [[HasType::{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasSuccessor&lt;br /&gt;
|?HasOutput&lt;br /&gt;
|?HasInputParameter&lt;br /&gt;
|?DependsOn&lt;br /&gt;
|format=datatable&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW725a3cf5458f4daea86615fcbd0029f8&amp;diff=8876</id>
		<title>Category:OSW725a3cf5458f4daea86615fcbd0029f8</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW725a3cf5458f4daea86615fcbd0029f8&amp;diff=8876"/>
		<updated>2025-02-26T04:36:33Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: render imported_from as link, render property name in restrictions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW725a3cf5458f4daea86615fcbd0029f8&amp;diff=7116</id>
		<title>Category:OSW725a3cf5458f4daea86615fcbd0029f8</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW725a3cf5458f4daea86615fcbd0029f8&amp;diff=7116"/>
		<updated>2025-02-25T07:53:44Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Ontology&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW57beed5e1294434ba77bb6516e461456&amp;diff=4093</id>
		<title>Category:OSW57beed5e1294434ba77bb6516e461456</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW57beed5e1294434ba77bb6516e461456&amp;diff=4093"/>
		<updated>2025-02-24T04:53:09Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Ontology&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:Entity&amp;diff=4083</id>
		<title>Category:Entity</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:Entity&amp;diff=4083"/>
		<updated>2025-02-22T04:33:01Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:Category&amp;diff=4082</id>
		<title>Category:Category</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:Category&amp;diff=4082"/>
		<updated>2025-02-22T04:32:02Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Module:MwJson&amp;diff=4081</id>
		<title>Module:MwJson</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Module:MwJson&amp;diff=4081"/>
		<updated>2025-02-22T04:31:19Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
&lt;br /&gt;
local lustache = require(&amp;quot;Module:Lustache&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local p = {} --p stands for package&lt;br /&gt;
&lt;br /&gt;
p.keys = { --jsonschema / json-ld keys&lt;br /&gt;
	category=&#039;type&#039;, &lt;br /&gt;
	category_pseudoproperty=&#039;Category&#039;, -- Property:Category&lt;br /&gt;
	subcategory=&#039;subclass_of&#039;,&lt;br /&gt;
	schema_type=&#039;schema_type&#039;,&lt;br /&gt;
	property_ns_prefix=&#039;Property&#039;,&lt;br /&gt;
	schema=&#039;osl_schema&#039;, &lt;br /&gt;
	template=&#039;eval_template&#039;,&lt;br /&gt;
	mode=&#039;mode&#039;,&lt;br /&gt;
	context=&#039;@context&#039;,&lt;br /&gt;
	allOf=&#039;allOf&#039;,&lt;br /&gt;
	label=&#039;label&#039;,&lt;br /&gt;
	name=&#039;name&#039;,&lt;br /&gt;
	description=&#039;description&#039;,&lt;br /&gt;
	text=&#039;text&#039;,&lt;br /&gt;
	debug=&#039;_debug&#039;&lt;br /&gt;
} &lt;br /&gt;
p.slots = { --slot names&lt;br /&gt;
	main=&#039;main&#039;,&lt;br /&gt;
	jsondata=&#039;jsondata&#039;, &lt;br /&gt;
	jsonschema=&#039;jsonschema&#039;, &lt;br /&gt;
	header_template=&#039;header_template&#039;,&lt;br /&gt;
	footer_template=&#039;footer_template&#039;,&lt;br /&gt;
	data_template=&#039;data_template&#039;&lt;br /&gt;
} &lt;br /&gt;
p.mode = {&lt;br /&gt;
	header=&#039;header&#039;,&lt;br /&gt;
	footer=&#039;footer&#039;,&lt;br /&gt;
	query=&#039;query&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
p.cache = {}&lt;br /&gt;
&lt;br /&gt;
--loads json from a wiki page&lt;br /&gt;
--test: mw.logObject(p.loadJson({title=&amp;quot;JsonSchema:Entity&amp;quot;}))&lt;br /&gt;
--test: mw.logObject(p.loadJson({title=&amp;quot;Category:Entity&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}))&lt;br /&gt;
function p.loadJson(args)&lt;br /&gt;
	local page_title = p.defaultArg(args.title, &amp;quot;JsonSchema:Entity&amp;quot;) --for testing&lt;br /&gt;
	local slot = p.defaultArg(args.slot, &#039;main&#039;)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, nil)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local json = {}&lt;br /&gt;
	&lt;br /&gt;
	if p.cache[page_title] ~= nil then&lt;br /&gt;
		if p.cache[page_title][slot] ~= nil then&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. p.slots.jsondata .. &amp;quot; of page &amp;quot; .. page_title .. &amp;quot; from cache &amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			json = p.cache[page_title][slot]&lt;br /&gt;
			return {json=json, debug_msg=msg}&lt;br /&gt;
		end&lt;br /&gt;
	else p.cache[page_title] = {}&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (slot == &#039;main&#039;) then&lt;br /&gt;
		--json = mw.loadJsonData( &amp;quot;JsonSchema:Entity&amp;quot; ) --requires MediaWiki 1.39&lt;br /&gt;
		local page = mw.title.makeTitle(p.splitString(page_title, &#039;:&#039;)[1], p.splitString(page_title, &#039;:&#039;)[2])&lt;br /&gt;
		local text = page:getContent()&lt;br /&gt;
		if (text ~= nil) then json = mw.text.jsonDecode(text) end&lt;br /&gt;
	else&lt;br /&gt;
		if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. p.slots.jsondata .. &amp;quot; of page &amp;quot; .. page_title .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		local text = mw.slots.slotContent( slot , page_title )&lt;br /&gt;
		if (text ~= nil) then json = mw.text.jsonDecode(text) end&lt;br /&gt;
	end	&lt;br /&gt;
	&lt;br /&gt;
	--mw.logObject(json)&lt;br /&gt;
	p.cache[page_title][slot] = json&lt;br /&gt;
&lt;br /&gt;
	return {json=json, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- test: mw.logObject(p.walkJsonSchema({jsonschema=p.loadJson({title=&amp;quot;Category:Hardware&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema)&lt;br /&gt;
function p.walkJsonSchema(args)&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local jsonschemas = p.defaultArg(args.jsonschemas, {})&lt;br /&gt;
	local categories = p.defaultArg(args.categories, nil)&lt;br /&gt;
	local visited = p.defaultArg(args.visited, {})&lt;br /&gt;
	local mode = p.defaultArg(args.mode, p.mode.header)&lt;br /&gt;
	--local merged_jsonschema = p.defaultArg(args.merged_jsonschema, {})&lt;br /&gt;
	local template = p.defaultArg(args.template, nil)&lt;br /&gt;
	local templates = p.defaultArg(args.templates, {})&lt;br /&gt;
	local recursive = p.defaultArg(args.recursive, true)&lt;br /&gt;
	local root = p.defaultArg(args.root, true)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local category_template_slot = nil&lt;br /&gt;
	if (mode == p.mode.footer) then category_template_slot = p.slots.footer_template end&lt;br /&gt;
	if (mode == p.mode.header) then category_template_slot = p.slots.header_template end&lt;br /&gt;
	&lt;br /&gt;
	if (categories == nil) then categories = p.getCategories({jsonschema=jsonschema, includeNamespace=true, includeSchemas=true}).categories end&lt;br /&gt;
	if (type(categories) ~= &#039;table&#039;) then categories = {categories} end&lt;br /&gt;
	if (debug) then msg = msg .. &amp;quot;Supercategories: &amp;quot; .. mw.dumpObject(categories) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	for k, category in pairs(categories) do&lt;br /&gt;
		if (not p.tableContains(visited, category)) then&lt;br /&gt;
			--mw.logObject(&amp;quot;Visit &amp;quot; .. category)&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. p.slots.jsonschema .. &amp;quot; from page &amp;quot; .. category .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			local super_jsonschema = nil&lt;br /&gt;
			if p.splitString(category, &#039;:&#039;)[1] == &amp;quot;JsonSchema&amp;quot; then super_jsonschema = p.loadJson({title=category, slot=p.slots.main}).json&lt;br /&gt;
			else super_jsonschema = p.loadJson({title=category, slot=p.slots.jsonschema}).json end&lt;br /&gt;
			if (super_jsonschema ~= nil) then&lt;br /&gt;
				if (recursive) then	&lt;br /&gt;
					local res = p.walkJsonSchema({jsonschema=super_jsonschema, jsonschemas=jsonschemas, templates=templates, mode=mode, visited=visited, root=false})&lt;br /&gt;
					wikitext = wikitext .. res.wikitext &lt;br /&gt;
				end&lt;br /&gt;
				--table.insert(jsonschemas, mw.text.jsonDecode( super_jsonschema_str )) --keep a copy of the schema, super_jsonschema passed by references gets modified&lt;br /&gt;
				--table.insert(jsonschemas, super_jsonschema ) &lt;br /&gt;
				--mw.logObject(&amp;quot;Store &amp;quot; .. category)&lt;br /&gt;
				table.insert(visited, category)&lt;br /&gt;
				&lt;br /&gt;
				jsonschemas[category] = p.copy( super_jsonschema ) --keep a copy of the schema, super_jsonschema passed by references gets modified&lt;br /&gt;
				--jsonschema = p.tableMerge(jsonschema, super_jsonschema) --merge superschema is done by the caller&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. category_template_slot .. &amp;quot; from page &amp;quot; .. category .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			templates[category] = mw.slots.slotContent( category_template_slot , category )&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	if (root) then&lt;br /&gt;
		for i, category in ipairs(visited) do&lt;br /&gt;
			--merge all schemas. we need to make a copy here, otherwise jsonschemas[&amp;quot;Category:Entity&amp;quot;] contains the merged schema&lt;br /&gt;
			jsonschema = p.copy(p.tableMerge(jsonschema, jsonschemas[category])) &lt;br /&gt;
		end	&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then wikitext = msg .. wikitext  end&lt;br /&gt;
	return {jsonschema=jsonschema, jsonschemas=jsonschemas, templates=templates, visited=visited, wikitext=wikitext}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[ test: &lt;br /&gt;
category = &amp;quot;Category:Hardware&amp;quot;&lt;br /&gt;
page = &amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;&lt;br /&gt;
category2 = &amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;&lt;br /&gt;
page2 = &amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;&lt;br /&gt;
mw.logObject(p.expandEmbeddedTemplates({&lt;br /&gt;
	jsonschema=p.walkJsonSchema({jsonschema=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema, &lt;br /&gt;
	jsondata=p.loadJson({title=page, slot=&amp;quot;jsondata&amp;quot;}).json,&lt;br /&gt;
	debug=true, mode=&amp;quot;render&amp;quot;&lt;br /&gt;
}).res)&lt;br /&gt;
--]]&lt;br /&gt;
function p.expandEmbeddedTemplates(args)&lt;br /&gt;
	local frame = p.defaultArg(args.frame, mw.getCurrentFrame())&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local template = p.defaultArg(args.template, nil)&lt;br /&gt;
	local mode = p.defaultArg(args.mode, nil)&lt;br /&gt;
	local stringify_arrays = p.defaultArg(args.stringify_arrays, false)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	local res = p.defaultArg(args.jsondata, &amp;quot;&amp;quot;)&lt;br /&gt;
	local root = p.defaultArg(args.root, true) -- first entry into recursion&lt;br /&gt;
	&lt;br /&gt;
	for k,v in pairs(jsondata) do&lt;br /&gt;
		local eval_template = nil&lt;br /&gt;
		local eval_templates = p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, p.keys.template}, {})&lt;br /&gt;
		if (eval_templates[1] == nil) then eval_templates = {eval_templates} end --ensure list of objects&lt;br /&gt;
		for i, t in pairs(eval_templates) do&lt;br /&gt;
			if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then eval_template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
			elseif (t[p.keys.mode] == nil) then  eval_template = t --default&lt;br /&gt;
			elseif (debug) then msg = msg .. &amp;quot;Ignore eval_template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if (eval_template ~= nil and eval_template.value ~= nil and (eval_template.type == &amp;quot;mustache&amp;quot; or eval_template.type == &amp;quot;mustache-wikitext&amp;quot;)) then&lt;br /&gt;
			-- mustache can handle objects and array to we can parse it directly&lt;br /&gt;
			-- todo: handle nested templates&lt;br /&gt;
			local template_param = {[k]=v}&lt;br /&gt;
			if (eval_template.root_key == false) then template_param = v end&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Parse mustache template &amp;quot; .. eval_template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( template_param ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			jsondata[k] = lustache:render(eval_template.value, template_param, p.tableMerge({self=eval_template.value}, eval_template.partials)) -- render with self as registered partial for recursion&lt;br /&gt;
			if (eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then &lt;br /&gt;
				jsondata[k] = frame:preprocess( jsondata[k] )&lt;br /&gt;
			end&lt;br /&gt;
		elseif type(v) == &#039;table&#039; then &lt;br /&gt;
			if (p.tableLength(v) &amp;gt; 0 and v[1] == nil) then --key value array = object/dict&lt;br /&gt;
				local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=v, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays, root=false})&lt;br /&gt;
				msg = msg .. sub_res.debug_msg&lt;br /&gt;
				jsondata[k] = sub_res.res&lt;br /&gt;
				--if (sub_res.unparsed ~= nil) then jsondata[k] = sub_res.unparsed else jsondata[k] = sub_res.wikitext end&lt;br /&gt;
			else --list array&lt;br /&gt;
				local string_list = &amp;quot;&amp;quot;&lt;br /&gt;
				for i,e in pairs(v) do &lt;br /&gt;
					&lt;br /&gt;
					local eval_template = nil&lt;br /&gt;
					local eval_templates = p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;, p.keys.template}, {})&lt;br /&gt;
					if (eval_templates[1] == nil) then eval_templates = {eval_templates} end --ensure list of objects&lt;br /&gt;
					&lt;br /&gt;
					for i, t in pairs(eval_templates) do&lt;br /&gt;
						if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then eval_template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
						elseif (t[p.keys.mode] == nil) then  eval_template = t --default&lt;br /&gt;
						elseif (debug) then msg = msg .. &amp;quot;Ignore eval_template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					if type(e) == &#039;table&#039; then 	&lt;br /&gt;
						local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=e, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays, root=false})&lt;br /&gt;
						msg = msg .. sub_res.debug_msg&lt;br /&gt;
						if (type(sub_res.res) == &#039;table&#039;) then &lt;br /&gt;
							if (debug) then msg = msg .. &amp;quot;Values for &amp;quot; .. k .. &amp;quot; contains non-literal items: &amp;quot; .. mw.dumpObject( sub_res.res ) .. &amp;quot; =&amp;gt; skip value in wikitemplate array param creation\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
						else &lt;br /&gt;
							if (stringify_arrays) then string_list = string_list .. sub_res.res .. &amp;quot;;&amp;quot; &lt;br /&gt;
							else v[i] = sub_res.res end&lt;br /&gt;
						end&lt;br /&gt;
					else&lt;br /&gt;
						if (eval_template ~= nil and eval_template.value ~= nil) then&lt;br /&gt;
							&lt;br /&gt;
							--evaluate single array item string as json {&amp;quot;self&amp;quot;: &amp;quot;&amp;lt;value&amp;gt;&amp;quot;, &amp;quot;.&amp;quot;: &amp;quot;&amp;lt;value&amp;gt;&amp;quot;} =&amp;gt; does not work since jsondata is an object&lt;br /&gt;
							--e = p.expandEmbeddedTemplates({frame=frame, jsondata={[&amp;quot;self&amp;quot;]=e,[&amp;quot;.&amp;quot;]=e}, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays, root=false})&lt;br /&gt;
							&lt;br /&gt;
&lt;br /&gt;
							if (eval_template.type == &amp;quot;mustache&amp;quot; or eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then&lt;br /&gt;
								if (debug) then msg = msg .. &amp;quot;Parse mustache template &amp;quot; .. eval_template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( e ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
								-- {{.}} in the template will be the value of e&lt;br /&gt;
								e = lustache:render(eval_template.value, e, p.tableMerge({self=eval_template.value}, eval_template.partials)) -- render with self as registered partial for recursion&lt;br /&gt;
							end&lt;br /&gt;
							if (eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then --or eval_template.type == &amp;quot;wikitext&amp;quot;) then &lt;br /&gt;
								if (debug) then msg = msg .. &amp;quot;Parse wikitext template &amp;quot; .. e .. &amp;quot; with params &amp;quot; .. mw.dumpObject( e ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
								e = frame:preprocess( e )&lt;br /&gt;
							end&lt;br /&gt;
							v[i] = e -- update array&lt;br /&gt;
						end&lt;br /&gt;
						if (stringify_arrays) then string_list = string_list .. e .. &amp;quot;;&amp;quot; end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				if (stringify_arrays) then jsondata[k] = string_list end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	if (template == nil and root == false) then -- don&#039;t stringify root json objects&lt;br /&gt;
		local templates = jsondata[p.keys.template]&lt;br /&gt;
		if (templates == nil) then templates = p.defaultArg(jsonschema[p.keys.template], {}) end&lt;br /&gt;
		if (templates[1] == nil) then templates = {templates} end --ensure list of objects&lt;br /&gt;
		for i, t in pairs(templates) do&lt;br /&gt;
			if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
			elseif (t[p.keys.mode] == nil) then  template = t --default&lt;br /&gt;
			elseif (debug) then msg = msg .. &amp;quot;Ignore template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (template ~= nil and root == false) then -- don&#039;t stringify root json objects&lt;br /&gt;
		if (template.type == &amp;quot;wikitext&amp;quot;) then&lt;br /&gt;
			for k,v in pairs(jsondata) do&lt;br /&gt;
				if type(v) == &#039;table&#039; then &lt;br /&gt;
					if (debug) then msg = msg .. &amp;quot;Values for &amp;quot; .. k .. &amp;quot; contains non-literals: &amp;quot; .. mw.dumpObject( v ) .. &amp;quot; =&amp;gt; skip wikitemplate parsing\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
					return {res=res, debug_msg=msg} &lt;br /&gt;
				end --not supported&lt;br /&gt;
			end			&lt;br /&gt;
			if (template.value ~= nil) then&lt;br /&gt;
				if (debug) then msg = msg .. &amp;quot;Parse wikitemplate &amp;quot; .. template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
				local child = frame:newChild{args=jsondata}&lt;br /&gt;
				res = child:preprocess( template.value )&lt;br /&gt;
			elseif (template.page ~= nil) then&lt;br /&gt;
				if (debug) then msg = msg .. &amp;quot;Parse wikitemplate &amp;quot; .. template.page .. &amp;quot; with params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
				res = frame:expandTemplate{ title = template.page, args = jsondata }&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--if (debug) then mw.logObject(msg) end&lt;br /&gt;
	return {res=res, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Category:OSWb3022bbf7e7146eb8e6f6e3264f50bbe&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;, categories={&amp;quot;Category:Category&amp;quot;}}))&lt;br /&gt;
function p.processJsondata(args)&lt;br /&gt;
	local frame = p.defaultArg(args.frame, mw.getCurrentFrame())&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local template = p.defaultArg(args.template, nil)&lt;br /&gt;
	local categories = p.defaultArg(args.categories, nil)&lt;br /&gt;
	local recursive = p.defaultArg(args.recursive, true)&lt;br /&gt;
	local mode = p.defaultArg(args.mode, p.mode.header)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local title = mw.title.getCurrentTitle()&lt;br /&gt;
	&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	local msg = &amp;quot;&amp;quot; --debug msg&lt;br /&gt;
&lt;br /&gt;
	if (p.nilOrEmpty(jsondata) or (p.nilOrEmpty(categories) and p.nilOrEmpty(jsonschema) and p.nilOrEmpty(jsondata[p.keys.category]))) then return {wikitext=wikitext, debug_msg=msg} end --nothing to do here&lt;br /&gt;
	--if (jsondata == nil or p.tableLength(jsondata) == 0 or (categories == nil and jsonschema == nil and jsondata[p.keys.category] == nil)) then return {wikitext=wikitext, debug_msg=msg} end --nothing to do here&lt;br /&gt;
	--jsonschema = p.defaultArg(jsonschema, {})&lt;br /&gt;
	--jsondata = p.defaultArg(jsondata, {})&lt;br /&gt;
	--if (categories == nil) then categories = jsondata[p.keys.category] end -- let function param overwrite json property&lt;br /&gt;
	if (not p.nilOrEmpty(jsondata[p.keys.category])) then categories = jsondata[p.keys.category] end -- let json property overwrite function param&lt;br /&gt;
	&lt;br /&gt;
	local schema_res = p.walkJsonSchema({jsonschema=jsonschema, categories=categories, mode=mode, recursive=recursive, debug=debug})&lt;br /&gt;
	local expand_res = p.expandJsonRef({json=schema_res.jsonschema, debug=debug})&lt;br /&gt;
	jsonschema = expand_res.json&lt;br /&gt;
	--mw.log(mw.text.jsonEncode(jsonschema))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	local jsonld = p.copy(jsondata)&lt;br /&gt;
	local json_data_store = p.copy(jsondata)&lt;br /&gt;
	local json_data_render = p.copy(jsondata)&lt;br /&gt;
	json_res_store = p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=json_data_store, mode=&#039;store&#039;})&lt;br /&gt;
	msg = msg .. json_res_store.debug_msg&lt;br /&gt;
	--mw.log(&amp;quot;JSONDATA STORE&amp;quot;)&lt;br /&gt;
	--mw.logObject(json_res_store.res)&lt;br /&gt;
	&lt;br /&gt;
	local smw_res = nil&lt;br /&gt;
	if (mode == p.mode.header) then&lt;br /&gt;
&lt;br /&gt;
		-- get the semantic properties by looking up the json keys in the json-ld context&lt;br /&gt;
		smw_res = p.getSemanticProperties({jsonschema=jsonschema, jsondata=json_res_store.res, store=false, debug=debug})&lt;br /&gt;
		&lt;br /&gt;
		-- store metadata where properties were defined / overridden&lt;br /&gt;
		for i, category in ipairs(schema_res.visited) do &lt;br /&gt;
			for k, v in pairs(p.defaultArgPath(schema_res.jsonschemas, {category, &#039;properties&#039;}, {})) do --property section may not exisit&lt;br /&gt;
				if smw_res.definitions[k] == nil then smw_res.definitions[k] = {} end&lt;br /&gt;
				if smw_res.definitions[k][&#039;defined_in&#039;] == nil then smw_res.definitions[k][&#039;defined_in&#039;] = {} end&lt;br /&gt;
				table.insert(smw_res.definitions[k][&#039;defined_in&#039;], category)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- embed json-ld in resulting html for search engine discovery&lt;br /&gt;
		jsonld[&amp;quot;@context&amp;quot;] = smw_res.context&lt;br /&gt;
		jsonld[&amp;quot;@type&amp;quot;] = p.tableMerge(p.tablefy(jsonschema.schema_type), p.tablefy(jsonld[&amp;quot;@type&amp;quot;])) --&lt;br /&gt;
		jsonld[&#039;schema:name&#039;] = p.defaultArgPath(jsonld, {p.keys.label, 1, p.keys.text}, jsonld[&#039;name&#039;]) --google does not support @value and @lang&lt;br /&gt;
		jsonld[&#039;schema:description&#039;] = p.defaultArgPath(jsonld, {p.keys.description, 1, p.keys.text}, nil)&lt;br /&gt;
		for k, v in pairs(jsonld) do&lt;br /&gt;
			if (type(v) == &amp;quot;string&amp;quot;) then&lt;br /&gt;
				local vpart = p.splitString(v, &#039;:&#039;)&lt;br /&gt;
				if (p.tableLength(vpart) == 2 and vpart[1] == &amp;quot;File&amp;quot;) then jsonld[k] = mw.getCurrentFrame():callParserFunction( &#039;filepath&#039;, { vpart[2] } ) end --google does not follow redirects via &amp;quot;File&amp;quot;:&amp;quot;wiki:Special:Redirect/file/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		wikitext = wikitext .. &amp;quot;&amp;lt;div class=&#039;jsonld-header&#039; style=&#039;display:none&#039; data-jsonld=&#039;&amp;quot; .. mw.text.jsonEncode( jsonld ):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;`&amp;quot;) .. &amp;quot;&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local json_res = p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=json_data_render, mode=&#039;render&#039;})&lt;br /&gt;
	msg = msg .. json_res.debug_msg&lt;br /&gt;
	jsondata =json_res.res&lt;br /&gt;
	--mw.log(&amp;quot;JSONDATA RENDER&amp;quot;)&lt;br /&gt;
	--mw.logObject(jsondata)&lt;br /&gt;
&lt;br /&gt;
	local max_index = p.tableLength(schema_res.visited)&lt;br /&gt;
	for i, category in ipairs(schema_res.visited) do&lt;br /&gt;
		if (mode == p.mode.footer) then category = schema_res.visited[max_index - i +1] end --reverse order for footer templates&lt;br /&gt;
		local super_jsonschema = schema_res.jsonschemas[category]&lt;br /&gt;
		local template = schema_res.templates[category]&lt;br /&gt;
		if (template ~= nil) then&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Parse \n\n&amp;quot; .. template .. &amp;quot; \n\nwith params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			local stripped_jsondata={}&lt;br /&gt;
			for k, v in pairs(jsondata) do&lt;br /&gt;
				if (type(v) ~= &#039;table&#039;) then stripped_jsondata[k] = v end --delete object values, not supported by wiki templates	&lt;br /&gt;
			end&lt;br /&gt;
			local child = frame:newChild{args=stripped_jsondata}&lt;br /&gt;
			if ( template:sub(1, #&amp;quot;=&amp;quot;) == &amp;quot;=&amp;quot; ) then template = &amp;quot;\n&amp;quot; .. template end -- add line break if template starts with heading (otherwise not rendered by mw parser)&lt;br /&gt;
			wikitext = wikitext .. child:preprocess( template )&lt;br /&gt;
		elseif (mode == p.mode.header) then&lt;br /&gt;
			local ignore_properties = {[p.keys.category]=true} -- don&#039;t render type/category on every subclass&lt;br /&gt;
			for j, subcategory in ipairs(schema_res.visited) do&lt;br /&gt;
				if j &amp;gt; i then&lt;br /&gt;
					local subjsonschema = schema_res.jsonschemas[subcategory]&lt;br /&gt;
					for k, v in pairs(p.defaultArg(subjsonschema[&#039;properties&#039;], {})) do&lt;br /&gt;
						-- skip properties that are overwritten in subschemas, render them only once at the most specific position&lt;br /&gt;
						ignore_properties[k] = true&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			-- render the infobox for the schema itself and every super_schema using always the global json-ld context (merged within walkJsonSchema())&lt;br /&gt;
			-- context needs to be preprocessed with buildContext() since the generic json/table merge of the @context atttribute produces a list of strings (remote context) and context objects&lt;br /&gt;
			-- context is already build in p.getSemanticProperties. schema_allOfMerged is used to provide the full schema for overridden properties&lt;br /&gt;
			local infobox_res = p.renderInfoBox({jsonschema=super_jsonschema, schema_allOfMerged=jsonschema, context=smw_res.context, property_definitions=smw_res.definitions, jsondata=jsondata, ignore_properties=ignore_properties})&lt;br /&gt;
			wikitext = wikitext .. frame:preprocess( infobox_res.wikitext )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--local display_label = &amp;quot;&amp;quot;&lt;br /&gt;
	--if (jsondata[p.keys.label] ~= nil) then display_label = p.splitString(jsondata[p.keys.label], &#039;@&#039;)[1] end&lt;br /&gt;
	local set_categories_in_wikitext = {}&lt;br /&gt;
	p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.subcategory])  --classes/categories, nil for items&lt;br /&gt;
	if (title.nsText ~= &amp;quot;Category&amp;quot;) then --items&lt;br /&gt;
		p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.category]) -- categories from schema type&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Todo: Consider moving the category and this block to p.getSemanticProperties with store=true. However, settings categories with @category is only possible for subobjects&lt;br /&gt;
	if (smw_res ~= nil) then&lt;br /&gt;
		local display_label = p.getDisplayLabel(jsondata, smw_res.properties)&lt;br /&gt;
		if title.nsText == &amp;quot;Property&amp;quot; then display_label = p.defaultArgPath(jsondata, {p.keys.name}, display_label) end&lt;br /&gt;
&lt;br /&gt;
		if (debug) then msg = msg .. &amp;quot;Store page properties&amp;quot; end&lt;br /&gt;
		-- category handling&lt;br /&gt;
		p.tableMerge(set_categories_in_wikitext, smw_res.properties[p.keys.category_pseudoproperty]) &lt;br /&gt;
		smw_res.properties[p.keys.category_pseudoproperty] = nil -- delete pseudo property&lt;br /&gt;
		&lt;br /&gt;
		smw_res.properties[&#039;HasOswId&#039;] = mw.title.getCurrentTitle().fullText  --set special property OswId to own title&lt;br /&gt;
		&lt;br /&gt;
		-- label and display title handling&lt;br /&gt;
		if display_label ~= nil then &lt;br /&gt;
			smw_res.properties[&#039;Display title of&#039;] = display_label --set special property display title&lt;br /&gt;
			smw_res.properties[&#039;Display title of lowercase&#039;] = display_label:lower() --store lowercase for case insensitive query&lt;br /&gt;
			smw_res.properties[&#039;Display title of normalized&#039;] = display_label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;) --store with all non-alphanumeric chars removed for normalized query&lt;br /&gt;
		end&lt;br /&gt;
		p.setNormalizedLabel(smw_res.properties) --build normalized multilang label&lt;br /&gt;
		mw.ext.displaytitle.set(display_label)&lt;br /&gt;
		--smw_res.properties[&#039;@category&#039;] = jsondata[p.keys.category]&lt;br /&gt;
		local store_res = mw.smw.set( smw_res.properties ) --store as semantic properties&lt;br /&gt;
		if (debug) then msg = msg .. mw.dumpObject(smw_res.properties) end&lt;br /&gt;
		if (store_res) then &lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;SMW SUCCESS: &amp;quot; end&lt;br /&gt;
		else&lt;br /&gt;
			wikitext = wikitext .. store_res.error &lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;SMW ERROR: &amp;quot; .. store_res.error end&lt;br /&gt;
		end&lt;br /&gt;
		--wikitext = mw.dumpObject(smw_res.properties) .. wikitext&lt;br /&gt;
	end&lt;br /&gt;
	wikitext = wikitext .. &amp;quot;\n&amp;quot; .. p.setCategories({categories=set_categories_in_wikitext, sortkey=display_label}).wikitext&lt;br /&gt;
	&lt;br /&gt;
	if (debug) then mw.logObject(res) end&lt;br /&gt;
	return {wikitext=wikitext, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- renders a default infobox&lt;br /&gt;
-- test: mw.logObject(p.renderInfoBox({jsonschema=p.loadJson({title=&amp;quot;JsonSchema:Entity&amp;quot;}).json, jsondata={uuid=&amp;quot;123123&amp;quot;}}))&lt;br /&gt;
function p.renderInfoBox(args)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, nil) -- local schema from the perspective of the current category&lt;br /&gt;
	local schema_allOfMerged = p.defaultArg(args.schema_allOfMerged, schema) -- global schema with allOfs merged&lt;br /&gt;
	local property_definitions = p.defaultArg(args.property_definitions, {}) -- dict schema_key: {property: &amp;lt;smw_property&amp;gt;, ...}&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	if schema == nil then return res end&lt;br /&gt;
	&lt;br /&gt;
	local context = p.defaultArg(args.context, p.buildContext({jsonschema=schema}).context)&lt;br /&gt;
	local ignore_properties = p.defaultArg(args.ignore_properties, {})&lt;br /&gt;
&lt;br /&gt;
	local schema_label = p.renderMultilangValue({jsonschema=schema})&lt;br /&gt;
	&lt;br /&gt;
	-- see also: https://help.fandom.com/wiki/Extension:Scribunto/HTML_Library_usage_notes&lt;br /&gt;
	local tbl = mw.html.create( &#039;table&#039; )&lt;br /&gt;
	tbl&lt;br /&gt;
		:attr( &#039;class&#039;, &#039;info_box&#039; )&lt;br /&gt;
		:tag( &#039;tr&#039; )&lt;br /&gt;
			:tag( &#039;th&#039; )&lt;br /&gt;
				:attr( &#039;class&#039;, &#039;heading&#039; )&lt;br /&gt;
				:attr( &#039;colspan&#039;, &#039;2&#039; )&lt;br /&gt;
				:wikitext( schema_label )&lt;br /&gt;
	for k,v in pairs(jsondata) do&lt;br /&gt;
		if (not ignore_properties[k]) then&lt;br /&gt;
			if (schema[&#039;properties&#039;] ~= nil and schema[&#039;properties&#039;][k] ~= nil and (type(v) ~= &#039;table&#039; or v[1] ~= nil)) then --literal or literal array&lt;br /&gt;
				local def = schema_allOfMerged[&#039;properties&#039;][k]&lt;br /&gt;
				--mw.logObject(def)&lt;br /&gt;
				&lt;br /&gt;
				local label = p.renderMultilangValue({jsonschema=def, default=k})&lt;br /&gt;
				&lt;br /&gt;
				local description = p.renderMultilangValue({jsonschema=def, key=&amp;quot;description&amp;quot;})&lt;br /&gt;
				if (p.tableLength(p.defaultArgPath(property_definitions, {k, &#039;defined_in&#039;}, {})) &amp;gt; 0) then description = description .. &amp;quot;&amp;lt;br&amp;gt;Definition: &amp;quot; end&lt;br /&gt;
				for i, c in pairs(p.defaultArgPath(property_definitions, {k, &#039;defined_in&#039;}, {})) do &lt;br /&gt;
					if (i &amp;gt; 1) then description = description .. &amp;quot;, &amp;quot; end&lt;br /&gt;
					description = description .. &amp;quot;[[:&amp;quot; ..c .. &amp;quot;]]&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
				if (description ~= &amp;quot;&amp;quot;) then description = &amp;quot;{{#info: &amp;quot; .. description .. &amp;quot;|note }}&amp;quot; end -- smw tooltip&lt;br /&gt;
				label = label .. description&lt;br /&gt;
&lt;br /&gt;
				--res = res .. title &amp;quot;: &amp;quot; .. v&lt;br /&gt;
				local cell = tbl:tag( &#039;tr&#039; )&lt;br /&gt;
									:tag( &#039;th&#039; )&lt;br /&gt;
										:wikitext( label )&lt;br /&gt;
										:done()&lt;br /&gt;
									:tag( &#039;td&#039; )&lt;br /&gt;
				if (type(v) == &#039;table&#039;) then&lt;br /&gt;
					for i,e in pairs(v) do &lt;br /&gt;
						if (type(e) ~= &#039;table&#039;) then &lt;br /&gt;
							local p_type = p.defaultArgPath(context, {k, &#039;@type&#039;}, &#039;@value&#039;)&lt;br /&gt;
							if (p_type == &#039;@id&#039; and p.defaultArgPath(def, {&#039;items&#039;, &#039;type&#039;}, &#039;unknown&#039;) == &#039;string&#039; and def[&#039;eval_template&#039;] == nil) then&lt;br /&gt;
								-- auto-link (OSW-)IDs if no eval_template is present&lt;br /&gt;
								e = string.gsub(e, &amp;quot;Category:&amp;quot;, &amp;quot;:Category:&amp;quot;) -- make sure category links work&lt;br /&gt;
								e = string.gsub(e, &amp;quot;File:&amp;quot;, &amp;quot;:File:&amp;quot;) -- do not embedd images but link to them&lt;br /&gt;
								e = &amp;quot;[[&amp;quot; .. e .. &amp;quot;]]&amp;quot; &lt;br /&gt;
							elseif (p_type == &#039;xsd:date&#039;) then -- formate date with user preferences&lt;br /&gt;
								e = &amp;quot;{{#dateformat:&amp;quot; .. e .. &amp;quot;|ymd}}&amp;quot; &lt;br /&gt;
							elseif (p_type == &#039;xsd:dateTime&#039;) then -- formate time with user preferences&lt;br /&gt;
								local smw_property = p.defaultArgPath(property_definitions, {k, &#039;property&#039;})&lt;br /&gt;
								if (smw_property ~= nil) then e = &amp;quot;{{#ask: [[{{FULLPAGENAME}}]]|?&amp;quot; .. smw_property .. &amp;quot;#LOCL#TO= |format=plain |mainlabel=-}}&amp;quot;&lt;br /&gt;
								else &lt;br /&gt;
									local _, _, date, hours, minutes = string.find(e, &amp;quot;(%S+)[T ](%S+)[:](%S+)[:?]&amp;quot;)&lt;br /&gt;
									e = &amp;quot;{{#dateformat:&amp;quot; .. date .. &amp;quot;|ymd}} &amp;quot; .. hours .. &amp;quot;:&amp;quot; .. minutes .. &amp;quot; (UTC)&amp;quot;&lt;br /&gt;
								end&lt;br /&gt;
							elseif (type(v) == &#039;boolean&#039;) then &lt;br /&gt;
								if (v) then v = &amp;quot;&amp;amp;#x2705;&amp;quot; else v = &amp;quot;&amp;amp;#x274C;&amp;quot; end -- green check mark or red cross&lt;br /&gt;
							elseif (def[&#039;eval_template&#039;] == nil and (string.len(e) &amp;gt; 100) and (string.find(e, &amp;quot;{{&amp;quot;) == nil) and (string.find(e, &amp;quot;&amp;lt;/&amp;quot;) == nil) and (string.find(e, &amp;quot;%[%[&amp;quot;) == nil)) then -- no markup, no links&lt;br /&gt;
								e = string.sub(e, 1, 100) .. &amp;quot;...&amp;quot;; -- limit infobox plain text to max 100 chars&lt;br /&gt;
							elseif (debug) then&lt;br /&gt;
								mw.log(&amp;quot;Unformated: &amp;quot; .. k .. &amp;quot; &amp;quot; .. p.defaultArgPath(def, {&#039;items&#039;, &#039;type&#039;}, &#039;unknown&#039;))&lt;br /&gt;
								mw.logObject(def)&lt;br /&gt;
							end&lt;br /&gt;
							cell:wikitext(&amp;quot;\n* &amp;quot; .. e .. &amp;quot;&amp;quot;) &lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					local p_type = p.defaultArgPath(context, {k, &#039;@type&#039;}, &#039;@value&#039;)&lt;br /&gt;
					if (p_type == &#039;@id&#039; and p.defaultArgPath(def, {&#039;type&#039;}, &#039;unknown&#039;) == &#039;string&#039; and def[&#039;eval_template&#039;] == nil) then &lt;br /&gt;
						-- auto-link (OSW-)IDs if no eval_template is present&lt;br /&gt;
						v = string.gsub(v, &amp;quot;Category:&amp;quot;, &amp;quot;:Category:&amp;quot;) -- make sure category links work&lt;br /&gt;
						v = string.gsub(v, &amp;quot;File:&amp;quot;, &amp;quot;:File:&amp;quot;) -- do not embedd images but link to them&lt;br /&gt;
						v = &amp;quot;[[&amp;quot; .. v .. &amp;quot;]]&amp;quot; &lt;br /&gt;
					elseif (p_type == &#039;xsd:date&#039;) then -- formate date &amp;amp; time with user preferences&lt;br /&gt;
						v = &amp;quot;{{#dateformat:&amp;quot; .. v .. &amp;quot;|ymd}}&amp;quot;&lt;br /&gt;
					elseif (p_type == &#039;xsd:dateTime&#039;) then -- formate time with user preferences&lt;br /&gt;
						local smw_property = p.defaultArgPath(property_definitions, {k, &#039;property&#039;})&lt;br /&gt;
						if (smw_property ~= nil) then v = &amp;quot;{{#ask: [[{{FULLPAGENAME}}]]|?&amp;quot; .. smw_property .. &amp;quot;#LOCL#TO= |format=plain |mainlabel=-}}&amp;quot;&lt;br /&gt;
						else &lt;br /&gt;
							local _, _, date, hours, minutes = string.find(v, &amp;quot;(%S+)[T ](%S+)[:](%S+)[:?]&amp;quot;)&lt;br /&gt;
							v = &amp;quot;{{#dateformat:&amp;quot; .. date .. &amp;quot;|ymd}} &amp;quot; .. hours .. &amp;quot;:&amp;quot; .. minutes .. &amp;quot; (UTC)&amp;quot;&lt;br /&gt;
						end&lt;br /&gt;
					elseif (type(v) == &#039;boolean&#039;) then &lt;br /&gt;
						if (v) then v = &amp;quot;&amp;amp;#x2705;&amp;quot; else v = &amp;quot;&amp;amp;#x274C;&amp;quot; end -- green check mark or red cross&lt;br /&gt;
					elseif (def[&#039;eval_template&#039;] == nil and (string.len(v) &amp;gt; 100) and (string.find(v, &amp;quot;{{&amp;quot;) == nil) and (string.find(v, &amp;quot;&amp;lt;/&amp;quot;) == nil) and (string.find(v, &amp;quot;%[%[&amp;quot;) == nil)) then -- no markup, no links&lt;br /&gt;
						v = string.sub(v, 1, 100) .. &amp;quot;...&amp;quot;; -- limit infobox plain text to max 100 chars&lt;br /&gt;
					elseif (debug) then&lt;br /&gt;
						mw.log(&amp;quot;Unformated: &amp;quot; .. k .. &amp;quot; &amp;quot; .. p.defaultArgPath(def, {&#039;type&#039;}, &#039;unknown&#039;))&lt;br /&gt;
						mw.logObject(def)&lt;br /&gt;
					end&lt;br /&gt;
					cell:wikitext(&amp;quot;\n&amp;quot; .. v .. &amp;quot;&amp;quot;)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	res = res .. tostring( tbl )&lt;br /&gt;
	--mw.logObject(res)&lt;br /&gt;
	&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- test&lt;br /&gt;
-- mw.logObject(p.getCategories({jsonschema={allOf={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test?action=raw&amp;amp;slot=jsonschema&amp;quot;}}, includeNamespace=true}))&lt;br /&gt;
-- mw.logObject(p.getCategories({jsonschema={allOf={{[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test?action=raw&amp;amp;slot=jsonschema&amp;quot;}, {[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test2?action=raw&amp;amp;slot=jsonschema&amp;quot;}}}}))&lt;br /&gt;
function p.getCategories(args)&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local includeNamespace = p.defaultArg(args.includeNamespace, false)&lt;br /&gt;
	local includeSchemas = p.defaultArg(args.includeSchemas, false)&lt;br /&gt;
	&lt;br /&gt;
	local categories = {}&lt;br /&gt;
		local allOf = jsonschema[p.keys.allOf]&lt;br /&gt;
		if (allOf ~= nil) then&lt;br /&gt;
			--properties[&#039;@category&#039;] = {}&lt;br /&gt;
			for k, entry in pairs(allOf) do&lt;br /&gt;
				local refs = nil&lt;br /&gt;
				if type(entry) == &#039;table&#039; then refs = entry -- &amp;quot;allOf&amp;quot;: [{&amp;quot;$ref&amp;quot;: &amp;quot;/wiki/Category:Test?action=raw&amp;quot;}]&lt;br /&gt;
				else refs = {entry} end-- &amp;quot;allOf&amp;quot;: {&amp;quot;$ref&amp;quot;: &amp;quot;/wiki/Category:Test?action=raw&amp;quot;}&lt;br /&gt;
				for p, v in pairs(entry) do&lt;br /&gt;
					if (p == &#039;$ref&#039;) then&lt;br /&gt;
						for category in v:gmatch(&amp;quot;Category:([^?]+)&amp;quot;) do -- e.g. &amp;quot;/wiki/Category:Test?action=raw&amp;quot;&lt;br /&gt;
							if (includeNamespace) then category = &amp;quot;Category:&amp;quot; .. category end&lt;br /&gt;
						    table.insert(categories, category)&lt;br /&gt;
						end&lt;br /&gt;
						if includeSchemas then&lt;br /&gt;
							for schema in v:gmatch(&amp;quot;JsonSchema:([^?]+)&amp;quot;) do -- e.g. &amp;quot;/wiki/JsonSchema:Test?action=raw&amp;quot;&lt;br /&gt;
								if (includeNamespace) then schema = &amp;quot;JsonSchema:&amp;quot; .. schema end&lt;br /&gt;
							    table.insert(categories, schema)&lt;br /&gt;
							end&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end	&lt;br /&gt;
		&lt;br /&gt;
	return {categories=categories}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--sets a list of categories on the page&lt;br /&gt;
--test: mw.logObject(p.setCategories({categories={&amp;quot;Cat1&amp;quot;, &amp;quot;Category:Cat2&amp;quot;}}))&lt;br /&gt;
function p.setCategories(args)&lt;br /&gt;
	local categories = p.defaultArg(args.categories, {})&lt;br /&gt;
	local sortkey = p.defaultArg(args.sortkey, &amp;quot;&amp;quot;)&lt;br /&gt;
	if (sortkey ~= &amp;quot;&amp;quot;) then sortkey = &amp;quot;|&amp;quot; .. sortkey end&lt;br /&gt;
	if (type(categories) ~= &#039;table&#039;) then categories = {categories} end&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	for k, entry in pairs(categories) do&lt;br /&gt;
		res = res .. &amp;quot;[[Category:&amp;quot; .. string.gsub(entry, &amp;quot;Category:&amp;quot;, &amp;quot;&amp;quot;) .. sortkey ..&amp;quot;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[ test&lt;br /&gt;
category = &amp;quot;Category:Entity&amp;quot;&lt;br /&gt;
jsonschema = p.expandJsonRef({json=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json}).json&lt;br /&gt;
mw.logObject(p.buildContext({jsonschema=jsonschema, debug=true}))&lt;br /&gt;
mw.log(mw.text.jsonEncode(p.buildContext({jsonschema=jsonschema, debug=false}).context))&lt;br /&gt;
or&lt;br /&gt;
jsonschema = {&lt;br /&gt;
	[&amp;quot;@context&amp;quot;]={test=&amp;quot;level 0&amp;quot;}, &lt;br /&gt;
	properties={&lt;br /&gt;
		test={&lt;br /&gt;
			type=&amp;quot;object&amp;quot;,&lt;br /&gt;
			[&amp;quot;@context&amp;quot;]={test1=&amp;quot;level 1&amp;quot;}, &lt;br /&gt;
			properties= {&lt;br /&gt;
				test= {&lt;br /&gt;
					type=&amp;quot;array&amp;quot;,&lt;br /&gt;
					items= {&lt;br /&gt;
						type=&amp;quot;object&amp;quot;,&lt;br /&gt;
						[&amp;quot;@context&amp;quot;]={test2=&amp;quot;level 2&amp;quot;}&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
mw.logObject(p.buildContext({jsonschema=jsonschema, debug=true}))&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- constructs a property specific local jsonld context&lt;br /&gt;
function p.buildContext(args)&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	--mw.logObject(schema)&lt;br /&gt;
	local context = p.defaultArg(args.context, schema[p.keys.context])&lt;br /&gt;
	local result = p.defaultArg(args.result, {})&lt;br /&gt;
	if (context ~= nil) then&lt;br /&gt;
		for k,v in pairs(context) do&lt;br /&gt;
			if type(k) == &#039;number&#039; and type(v) == &#039;string&#039; then&lt;br /&gt;
				--table.insert(result, v) --skip context imports&lt;br /&gt;
			elseif (type(v) == &#039;table&#039; and v[1] ~= nil) then --custom addtional mappings, e. g. &amp;quot;type*&amp;quot;: [&amp;quot;Property:HasType&amp;quot;]&lt;br /&gt;
				result[k] = v&lt;br /&gt;
			elseif (type(v) == &#039;table&#039; and v[&#039;@id&#039;] == nil and v[&#039;@reverse&#039;] == nil) then --subcontext&lt;br /&gt;
				p.tableMerge(result, p.buildContext({context=v}).context)&lt;br /&gt;
			else &lt;br /&gt;
				result[k] = v	&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local properties = p.defaultArg(schema.properties, {})&lt;br /&gt;
&lt;br /&gt;
	-- build property context&lt;br /&gt;
	for k,v in pairs(properties) do&lt;br /&gt;
		local subcontext = nil&lt;br /&gt;
		if (p.defaultArgPath(properties, {k, &#039;type&#039;}) == &#039;object&#039;) then&lt;br /&gt;
			--mw.logObject(properties[k])&lt;br /&gt;
			subcontext = p.buildContext({jsonschema=properties[k]}).context&lt;br /&gt;
		elseif (p.defaultArgPath(properties, {k, &#039;items&#039;, &#039;type&#039;}) == &#039;object&#039;) then &lt;br /&gt;
			--mw.logObject(properties[k][&#039;items&#039;])&lt;br /&gt;
			subcontext = p.buildContext({jsonschema=properties[k][&#039;items&#039;]}).context&lt;br /&gt;
		end&lt;br /&gt;
		if (subcontext ~= nil and p.tableLength(subcontext) &amp;gt; 0) then&lt;br /&gt;
			if (result[k] == nil) then result[k] = {} end&lt;br /&gt;
			if (type(result[k]) == &#039;string&#039;) then result[k] = {[&amp;quot;@id&amp;quot;]=result[k]} end&lt;br /&gt;
			if (result[k][p.keys.context] == nil) then result[k][p.keys.context] = {} end&lt;br /&gt;
			result[k][p.keys.context] = p.tableMerge(result[k][p.keys.context], subcontext)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return {context=result}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--maps jsondata values to semantic properties by using the @context attribute within the schema&lt;br /&gt;
--test: mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={test=&amp;quot;Property:schema:TestProperty&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}, jsondata={test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;}, debug=true}))&lt;br /&gt;
--test: mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={&amp;quot;some uri&amp;quot;,{test=&amp;quot;Property:TestProperty&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}}, jsondata={test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;}, debug=true}))&lt;br /&gt;
--[[&lt;br /&gt;
mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={test=&amp;quot;Property:TestProperty&amp;quot;, subobject=&amp;quot;Property:HasSubobject&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}, jsondata={&lt;br /&gt;
test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;, subobject={uuid=&amp;quot;123-123-123&amp;quot;, test=&amp;quot;TestValue2&amp;quot;}&lt;br /&gt;
}, debug=true}))&lt;br /&gt;
&lt;br /&gt;
mw.logObject(p.getSemanticProperties({jsonschema=p.loadJson({title=&amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}).json, p.loadJson({title=&amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;, slot=&amp;quot;jsonsdata&amp;quot;}).json, debug=true}))&lt;br /&gt;
&lt;br /&gt;
category = &amp;quot;Category:Hardware&amp;quot;&lt;br /&gt;
page = &amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;&lt;br /&gt;
category2 = &amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;&lt;br /&gt;
page2 = &amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;&lt;br /&gt;
jsonschema =p.walkJsonSchema({jsonschema=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema&lt;br /&gt;
mw.logObject(p.getSemanticProperties({&lt;br /&gt;
	jsonschema=jsonschema,&lt;br /&gt;
	jsondata=p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=p.loadJson({title=page, slot=&amp;quot;jsondata&amp;quot;}).json}).res,&lt;br /&gt;
	debug=true&lt;br /&gt;
}).properties)&lt;br /&gt;
&lt;br /&gt;
--]]&lt;br /&gt;
function p.getSemanticProperties(args)&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local subschema = p.defaultArg(args.subschema, schema)&lt;br /&gt;
	local parent_schema_property = p.defaultArg(args.parent_schema_property, {}) -- ToDo: Not used except in getSemanticQuery =&amp;gt; remove&lt;br /&gt;
	local store = p.defaultArg(args.store, false)&lt;br /&gt;
	local root = p.defaultArg(args.root, true)&lt;br /&gt;
	local properties = p.defaultArg(args.properties, {}) --semantic properties to store, dict key=property_name, value=array of string values&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	--if (debug) then mw.logObject(&amp;quot;Call getSemanticProperties with args &amp;quot; .. mw.dumpObject( args ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;) end&lt;br /&gt;
	&lt;br /&gt;
	local subjectId = mw.title.getCurrentTitle().fullText&lt;br /&gt;
	local subobjectId = nil&lt;br /&gt;
	if (root == false and jsondata[&#039;uuid&#039;] ~= nil) then &lt;br /&gt;
		subobjectId = &amp;quot;OSW&amp;quot; .. string.gsub(jsondata[&#039;uuid&#039;], &amp;quot;-&amp;quot;, &amp;quot;&amp;quot;) &lt;br /&gt;
		subjectId = subjectId .. &#039;#&#039; .. subobjectId&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local property_data = {}&lt;br /&gt;
	local context = p.defaultArg(args.context, p.buildContext({jsonschema=schema}).context)&lt;br /&gt;
	local error = &amp;quot;&amp;quot;&lt;br /&gt;
	if (debug) then mw.logObject(context) end&lt;br /&gt;
	if schema ~= nil and context ~= nil then&lt;br /&gt;
		local schema_properties = p.defaultArg(subschema.properties, {})&lt;br /&gt;
		if (debug and root) then&lt;br /&gt;
			for k,v in pairs(context) do&lt;br /&gt;
				if type(k) == &#039;number&#039; then mw.logObject(&amp;quot;imports &amp;quot; .. v)&lt;br /&gt;
				elseif type(v) == &#039;table&#039; and v[&amp;quot;@id&amp;quot;] ~= nil then mw.logObject(&amp;quot;&amp;quot; .. k .. &amp;quot; maps to &amp;quot; .. v[&amp;quot;@id&amp;quot;]) &lt;br /&gt;
				else mw.logObject(&amp;quot;&amp;quot; .. k .. &amp;quot; maps to &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		for k,v in pairs(jsondata) do&lt;br /&gt;
			local property_names = {}&lt;br /&gt;
			local subobject_properties = {} -- reverse properties to store in the subobject&lt;br /&gt;
			local mapping_found = false&lt;br /&gt;
			local property_definitions = {} -- list of objects {id=..., reverse=...}&lt;br /&gt;
&lt;br /&gt;
			for term, def in pairs(context) do&lt;br /&gt;
				local term_parts = p.splitString(term, &amp;quot;*&amp;quot;)&lt;br /&gt;
				if (term_parts[1] == k) then --custom additional mapping term*(*...): &amp;quot;Property:...&amp;quot;&lt;br /&gt;
					if type(def) == &#039;table&#039; then &lt;br /&gt;
						-- note: json-ld allows only @id OR @reverse&lt;br /&gt;
						if (def[&amp;quot;@id&amp;quot;] ~= nil) then table.insert(property_definitions, {id=def[&amp;quot;@id&amp;quot;], reverse=false}) end&lt;br /&gt;
						if (def[&amp;quot;@reverse&amp;quot;] ~= nil) then table.insert(property_definitions, {id=def[&amp;quot;@reverse&amp;quot;], reverse=true}) end&lt;br /&gt;
					else table.insert(property_definitions, {id=def}) end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if (debug) then mw.logObject(property_definitions) end&lt;br /&gt;
			for i,e in ipairs(property_definitions) do &lt;br /&gt;
				local id = e[&amp;quot;id&amp;quot;]&lt;br /&gt;
				local property_definition = p.splitString(id, &#039;:&#039;)&lt;br /&gt;
				if property_definition[1] == p.keys.property_ns_prefix then&lt;br /&gt;
					mapping_found = true&lt;br /&gt;
					property_name = string.gsub(id, p.keys.property_ns_prefix .. &amp;quot;:&amp;quot;, &amp;quot;&amp;quot;) -- also allow prefix properties like: Property:schema:url&lt;br /&gt;
					if (e[&amp;quot;reverse&amp;quot;]) then -- reverse properties are handled in the respective subobject&lt;br /&gt;
						if (subobject_properties[property_name] == nil) then subobject_properties[property_name] = {} end --initialize empty list&lt;br /&gt;
						table.insert(subobject_properties[property_name], subjectId) -- add triple subobject -property-&amp;gt; subject&lt;br /&gt;
					else table.insert(property_names, property_name) end&lt;br /&gt;
					local schema_property = p.defaultArg(schema_properties[k], {})&lt;br /&gt;
					local schema_type = p.defaultArg(schema_property.type, nil) --todo: also load smw property type on demand&lt;br /&gt;
					property_data[k] = {schema_type=schema_type, schema_data=schema_property, property=property_name, value=v, reverse=e[&amp;quot;reverse&amp;quot;]}&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			for i, property_name in ipairs(property_names) do&lt;br /&gt;
				if (properties[property_name] == nil) then properties[property_name] = {} end --initialize empty list&lt;br /&gt;
			end&lt;br /&gt;
			if type(v) == &#039;table&#039; then &lt;br /&gt;
				--if (debug) then mw.logObject(&amp;quot;prop &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
				if (mapping_found) then&lt;br /&gt;
					local subcontext = p.copy(p.defaultArgPath(context, {k, p.keys.context}, {})) --deepcopy, see also https://phabricator.wikimedia.org/T269990&lt;br /&gt;
					context = p.tableMerge(context, subcontext) -- pull up nested context&lt;br /&gt;
					local values = {}&lt;br /&gt;
					if (p.tableLength(v) &amp;gt; 0 and v[1] == nil) then --key value array = object/dict =&amp;gt; subobject&lt;br /&gt;
						local subproperties_res = p.getSemanticProperties({jsonschema=schema, jsondata=v, properties=p.copy(subobject_properties), store=true, root=false, debug=debug, context=context, subschema=schema_properties[k], parent_schema_property=property_data[k]})&lt;br /&gt;
						local id = subproperties_res.id --subobject_id&lt;br /&gt;
						if (id ~= nil) then &lt;br /&gt;
							id = mw.title.getCurrentTitle().fullText .. &#039;#&#039; .. id&lt;br /&gt;
							table.insert(values, id) &lt;br /&gt;
						end&lt;br /&gt;
						properties = p.processStatement({subject=properties, statement=subproperties_res.properties, debug=debug}).subject&lt;br /&gt;
					else --list array&lt;br /&gt;
						for i, e in pairs(v) do&lt;br /&gt;
							if (type(e) == &#039;table&#039;) then &lt;br /&gt;
								local subproperties_res = p.getSemanticProperties({jsonschema=schema, jsondata=e, properties=p.copy(subobject_properties), store=true, root=false, debug=debug, context=context, subschema=schema_properties[k], parent_schema_property=property_data[k]})&lt;br /&gt;
								local id = subproperties_res.id --subobject_id&lt;br /&gt;
								if (id ~= nil) then &lt;br /&gt;
									id = mw.title.getCurrentTitle().fullText .. &#039;#&#039; .. id&lt;br /&gt;
									table.insert(values, id) &lt;br /&gt;
								end&lt;br /&gt;
								properties = p.processStatement({subject=properties, statement=subproperties_res.properties, debug=debug}).subject&lt;br /&gt;
							else values = v end --plain strings&lt;br /&gt;
						end&lt;br /&gt;
					end &lt;br /&gt;
					for pi, property_name in ipairs(property_names) do&lt;br /&gt;
						for i,value in pairs(values) do table.insert(properties[property_name], value) end&lt;br /&gt;
						if (debug) then mw.logObject(&amp;quot;set &amp;quot; .. property_name .. &amp;quot; = &amp;quot; .. mw.dumpObject(values)) end&lt;br /&gt;
					end&lt;br /&gt;
				else if (debug) then mw.logObject(&amp;quot;not mapped: &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end &lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				if (mapping_found) then &lt;br /&gt;
					for pi, property_name in ipairs(property_names) do&lt;br /&gt;
						table.insert(properties[property_name], v)&lt;br /&gt;
						if (debug) then mw.logObject(&amp;quot;set &amp;quot; .. property_name .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
					end&lt;br /&gt;
				else &lt;br /&gt;
					if (debug) then mw.logObject(&amp;quot;not mapped: &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end &lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local store_res = nil&lt;br /&gt;
	if (store) then &lt;br /&gt;
		properties[&#039;HasOswId&#039;] = subjectId&lt;br /&gt;
		if (root) then &lt;br /&gt;
			if (debug) then mw.logObject(&amp;quot;Store page properties&amp;quot;) end&lt;br /&gt;
			store_res = mw.smw.set( properties ) --store as semantic properties&lt;br /&gt;
		else&lt;br /&gt;
			properties[&#039;@category&#039;] = {}&lt;br /&gt;
			p.tableMerge(properties[&#039;@category&#039;], jsondata[p.keys.category]) -- from json property &#039;type&#039;&lt;br /&gt;
			p.tableMerge(properties[&#039;@category&#039;], properties[p.keys.category_pseudoproperty]) -- from json-ld context &#039;Property:Category&#039;&lt;br /&gt;
			properties[p.keys.category_pseudoproperty] = nil -- delete pseudo property&lt;br /&gt;
			&lt;br /&gt;
			local display_label = p.getDisplayLabel(jsondata, properties)&lt;br /&gt;
			if properties[&#039;Display title of&#039;] == nil and properties[&#039;Display_title_of&#039;] == nil then&lt;br /&gt;
				if (display_label ~= nil and display_label ~= &amp;quot;&amp;quot;) then properties[&#039;Display title of&#039;] = display_label&lt;br /&gt;
				else properties[&#039;Display title of&#039;] = p.defaultArg(subschema[&#039;title&#039;], &amp;quot;&amp;quot;) end -- fall back to property name in schema&lt;br /&gt;
			end&lt;br /&gt;
			p.setNormalizedLabel(properties) --build normalized multilang label&lt;br /&gt;
			if (p.tableLength(properties) &amp;gt; 0) then&lt;br /&gt;
				store_res = mw.smw.subobject( properties, subobjectId )	--store as subobject&lt;br /&gt;
				if (debug) then mw.logObject(&amp;quot;Store subobject with id &amp;quot; .. (subobjectId or &amp;quot;&amp;lt;random&amp;gt;&amp;quot;)) end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then mw.logObject(properties) end&lt;br /&gt;
	if (store_res ~= nil) then &lt;br /&gt;
		if (not store_res and store_res.error ~= nil) then error = error .. store_res.error end&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then mw.logObject(error) end&lt;br /&gt;
	return {properties=properties, definitions=property_data, id=subobjectId, context=context, error=error}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.processStatement(args)&lt;br /&gt;
	local statement = p.defaultArg(args.statement)&lt;br /&gt;
	local subject = p.defaultArg(args.subject)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
&lt;br /&gt;
	-- handle &amp;quot;approved&amp;quot; statements&lt;br /&gt;
	if (statement[&amp;quot;HasSubject&amp;quot;] == nil or statement[&amp;quot;HasSubject&amp;quot;][1] == nil or statement[&amp;quot;HasSubject&amp;quot;][1] == &amp;quot;&amp;quot;) then --implicit subject&lt;br /&gt;
		if (statement[&amp;quot;HasProperty&amp;quot;] ~= nil and statement[&amp;quot;HasProperty&amp;quot;][1] ~= nil and statement[&amp;quot;HasProperty&amp;quot;][1] ~= &amp;quot;&amp;quot; and statement[&amp;quot;HasObject&amp;quot;] ~= nil) then&lt;br /&gt;
			local property = string.gsub(statement[&amp;quot;HasProperty&amp;quot;][1], p.keys.property_ns_prefix .. &amp;quot;:&amp;quot;, &amp;quot;&amp;quot;) -- also allow prefix properties like: Property:schema:url&lt;br /&gt;
			if (debug) then&lt;br /&gt;
				mw.log(&amp;quot;Set property &amp;quot; .. property .. &amp;quot; from statement to &amp;quot;)&lt;br /&gt;
				mw.logObject(statement[&amp;quot;HasObject&amp;quot;])&lt;br /&gt;
			end&lt;br /&gt;
			if (subject[property] == nil) then subject[property] = {} end&lt;br /&gt;
			for k, v in pairs(statement[&amp;quot;HasObject&amp;quot;]) do table.insert(subject[property], v) end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return {subject=subject}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- build a semantic query based on provided properties and their schema definition&lt;br /&gt;
--[[ test: &lt;br /&gt;
mw.logObject(p.getSemanticQuery({&lt;br /&gt;
	jsonschema={&lt;br /&gt;
		[&amp;quot;@context&amp;quot;]={&lt;br /&gt;
			test=&amp;quot;Property:TestProperty&amp;quot;,&lt;br /&gt;
			number_max=&amp;quot;Property:HasNumber&amp;quot;,&lt;br /&gt;
			date_min=&amp;quot;Property:HasDate&amp;quot;&lt;br /&gt;
		}, &lt;br /&gt;
		properties={&lt;br /&gt;
			test={title=&amp;quot;Test&amp;quot;, type=&amp;quot;string&amp;quot;},&lt;br /&gt;
			number_max={title=&amp;quot;Number&amp;quot;, type=&amp;quot;string&amp;quot;, format=&amp;quot;number&amp;quot;, options={role={query={filter=&amp;quot;max&amp;quot;}}}},&lt;br /&gt;
			date_min={title=&amp;quot;Date&amp;quot;, type=&amp;quot;string&amp;quot;, format=&amp;quot;date&amp;quot;, options={role={query={filter=&amp;quot;min&amp;quot;}}}},&lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	jsondata={test=&amp;quot;TestValue&amp;quot;, number_max=5, date_min=&amp;quot;01.01.2023&amp;quot;}&lt;br /&gt;
}))&lt;br /&gt;
--]]&lt;br /&gt;
function p.getSemanticQuery(args)&lt;br /&gt;
	--local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	--local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	local where = &amp;quot;&amp;quot;&lt;br /&gt;
	local select = &amp;quot;&amp;quot;&lt;br /&gt;
	local semantic_properties = p.getSemanticProperties(args)&lt;br /&gt;
	--mw.logObject(semantic_properties)&lt;br /&gt;
	for k,def in pairs(semantic_properties.definitions) do&lt;br /&gt;
		-- see also: https://www.semantic-mediawiki.org/wiki/Help:Search_operators&lt;br /&gt;
		local filter = p.defaultArgPath(def.schema_data, {&#039;options&#039;, &#039;role&#039;, &#039;query&#039;, &#039;filter&#039;}, &#039;eq&#039;)&lt;br /&gt;
		local value = def.value&lt;br /&gt;
		if def.schema_data.type == &#039;string&#039; and (def.schema_data.format == &#039;number&#039; or def.schema_data.format == &#039;date&#039;) then &lt;br /&gt;
			if (filter == &#039;min&#039;) then value = &amp;quot;&amp;lt;&amp;quot; .. value&lt;br /&gt;
			elseif (filter == &#039;max&#039;) then value = &amp;quot;&amp;gt;&amp;quot; .. value&lt;br /&gt;
			else value = value end --exact match&lt;br /&gt;
		elseif def.schema_data.type == &#039;string&#039; then&lt;br /&gt;
			value = &amp;quot;~*&amp;quot; .. value .. &amp;quot;*&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		where = where .. &amp;quot;\n[[&amp;quot;.. def.property .. &amp;quot;::&amp;quot; .. value .. &amp;quot;]]&amp;quot;&lt;br /&gt;
		select = select .. &amp;quot;\n|?&amp;quot; .. def.property&lt;br /&gt;
		if (def.schema_data.title ~= nil) then select = select .. &amp;quot;=&amp;quot; .. def.schema_data.title end&lt;br /&gt;
	end&lt;br /&gt;
	if (where ~= &amp;quot;&amp;quot;) then res = &amp;quot;{{#ask:&amp;quot; .. res .. where .. select .. &amp;quot;}}&amp;quot; end&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- HELPERS&lt;br /&gt;
&lt;br /&gt;
-- expands all $ref&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={items={test=&amp;quot;value&amp;quot;, [&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/JsonSchema:Label?action=raw&amp;quot;}}}).json)&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Item?action=raw&amp;amp;slot=jsonschema&amp;quot;}}).json)&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/JsonSchema:Statement?action=raw&amp;quot;}}).json)&lt;br /&gt;
function p.expandJsonRef(args)&lt;br /&gt;
	local json = p.defaultArg(args.json, {})&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local refs = {}&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if (k == &amp;quot;$ref&amp;quot;) then&lt;br /&gt;
    		-- e. g. &amp;quot;/wiki/JsonSchema:Label?action=raw&amp;quot; or &amp;quot;/wiki/Category:Entity?action=raw&amp;amp;slot=jsonschema&amp;quot;&lt;br /&gt;
    		if string.find(v, &amp;quot;#&amp;quot;) then&lt;br /&gt;
    			if (debug) then mw.logObject(&amp;quot;Skip relative reference&amp;quot;) end&lt;br /&gt;
    		else&lt;br /&gt;
	    		local uri = mw.uri.new(v)&lt;br /&gt;
	    		local ref_title = mw.text.split(uri.path, &amp;quot;wiki/&amp;quot;, true)[2]&lt;br /&gt;
	    		local ref_slot = uri.query[&amp;quot;slot&amp;quot;]&lt;br /&gt;
	    		if (debug) then &lt;br /&gt;
		    		if (ref_slot ~= nil) then mw.logObject(&amp;quot;Ref found with title &amp;quot; .. ref_title .. &amp;quot; and slot &amp;quot; .. ref_slot)&lt;br /&gt;
		    		else mw.logObject(&amp;quot;Ref found with title &amp;quot; .. ref_title) end&lt;br /&gt;
	    		end&lt;br /&gt;
	    		local ref_json = p.loadJson({title=ref_title, slot=ref_slot}).json&lt;br /&gt;
	    		refs[v] = ref_json&lt;br /&gt;
	    		json[k] = nil&lt;br /&gt;
    		end&lt;br /&gt;
    	end&lt;br /&gt;
    end&lt;br /&gt;
	--mw.logObject(refs)&lt;br /&gt;
	for k,v in pairs(refs) do&lt;br /&gt;
		json = p.tableMerge(v, json)&lt;br /&gt;
	end&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
            json[k] = p.expandJsonRef({json=v}).json&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    local result = p.copy(json)&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if (k == &amp;quot;allOf&amp;quot;) then&lt;br /&gt;
            if (type(v) == &amp;quot;table&amp;quot; and v[1] == nil) then v = {v} end -- ensure array&lt;br /&gt;
            for i,s in pairs(v) do &lt;br /&gt;
            	result = p.tableMerge(s, result)&lt;br /&gt;
            	if (debug) then mw.log(&amp;quot;merge allOf with title &amp;quot; .. s[&amp;quot;title&amp;quot;]) end&lt;br /&gt;
            end&lt;br /&gt;
            result[k] = nil&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return {json=result}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function p.defaultArg(arg, default)&lt;br /&gt;
	if (arg == nil) then &lt;br /&gt;
		return default&lt;br /&gt;
	else&lt;br /&gt;
		return arg&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- returns the value of a table (dict) path or default, if the path is not defined&lt;br /&gt;
-- test: mw.logObject(p.defaultArgPath({some={defined={path=&amp;quot;value&amp;quot;}}}, {&amp;quot;some&amp;quot;, &amp;quot;defined&amp;quot;, &amp;quot;path&amp;quot;}, &amp;quot;default_value&amp;quot;))&lt;br /&gt;
-- test: mw.logObject(p.defaultArgPath({some={defined={path=&amp;quot;value&amp;quot;}}}, {&amp;quot;some&amp;quot;, &amp;quot;undefined&amp;quot;, &amp;quot;path&amp;quot;}, &amp;quot;default_value&amp;quot;))&lt;br /&gt;
function p.defaultArgPath(arg, path, default)&lt;br /&gt;
	if (arg == nil) then &lt;br /&gt;
		return default&lt;br /&gt;
	elseif (path == nil) then&lt;br /&gt;
		return arg&lt;br /&gt;
	else&lt;br /&gt;
		key = table.remove(path,1)&lt;br /&gt;
		if (key == nil) then return arg end  --end of path&lt;br /&gt;
		return p.defaultArgPath(arg[key], path, default)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.splitString(inputstr, sep)&lt;br /&gt;
	&lt;br /&gt;
        if sep == nil then&lt;br /&gt;
                sep = &amp;quot;;&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        local t={}&lt;br /&gt;
        for str in string.gmatch(inputstr, &amp;quot;([^&amp;quot;..sep..&amp;quot;]+)&amp;quot;) do&lt;br /&gt;
                table.insert(t, str)&lt;br /&gt;
        end&lt;br /&gt;
        return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--dumps a table to a string (replaced by mw.dumpObject())&lt;br /&gt;
function p.dump(o)&lt;br /&gt;
   return mw.dumpObject(o)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--converts a literal to an table&lt;br /&gt;
function p.tablefy(o)&lt;br /&gt;
	if (o == nil) then o = {} end&lt;br /&gt;
	if (type(o) ~= &#039;table&#039;) then o = {o} end&lt;br /&gt;
	return o&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--true if the value is contained in the array (flat arrays only)&lt;br /&gt;
function p.tableContains (tab, val)&lt;br /&gt;
    for index, value in ipairs(tab) do&lt;br /&gt;
        if value == val then&lt;br /&gt;
            return true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--get the size of a table&lt;br /&gt;
function p.tableLength(t)&lt;br /&gt;
  local count = 0&lt;br /&gt;
  for _ in pairs(t) do count = count + 1 end&lt;br /&gt;
  return count&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--check if a variable is nil or an empty string or table&lt;br /&gt;
function p.nilOrEmpty(o)&lt;br /&gt;
	if (o == nil) then return true&lt;br /&gt;
	elseif (type(o) == &#039;string&#039; and o == &amp;quot;&amp;quot;) then return true&lt;br /&gt;
	elseif (type(o) == &#039;table&#039; and p.tableLength(o) == 0) then return true&lt;br /&gt;
	else return false &lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- merges t2 to t1&lt;br /&gt;
--test: mw.logObject(p.tableMerge({&amp;quot;string&amp;quot;, test1=&amp;quot;test1&amp;quot;, subtable1={&amp;quot;test&amp;quot;}}, {&amp;quot;string2&amp;quot;, test1=&amp;quot;test2&amp;quot;, test3=&amp;quot;test4&amp;quot;}))&lt;br /&gt;
function p.tableMerge(t1, t2)&lt;br /&gt;
	if (t1 == nil) then t1 = {} elseif (type(t1) ~= &#039;table&#039;) then t1 = {t1} end&lt;br /&gt;
	if (t2 == nil) then t2 = {} elseif (type(t2) ~= &#039;table&#039;) then t2 = {t2} end&lt;br /&gt;
    for k,v in pairs(t2) do&lt;br /&gt;
        if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
            if type(t1[k] or false) == &amp;quot;table&amp;quot; then&lt;br /&gt;
                p.tableMerge(t1[k] or {}, t2[k] or {})&lt;br /&gt;
            else&lt;br /&gt;
                if type(k) == &#039;number&#039; then table.insert(t1, v)&lt;br /&gt;
            	else t1[k] = v end&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
        	if type(k) == &#039;number&#039; then table.insert(t1, v)&lt;br /&gt;
            else t1[k] = v end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return t1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- from https://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value&lt;br /&gt;
function p.copy(obj, seen)&lt;br /&gt;
  if type(obj) ~= &#039;table&#039; then return obj end&lt;br /&gt;
  if seen and seen[obj] then return seen[obj] end&lt;br /&gt;
  local s = seen or {}&lt;br /&gt;
  local res = setmetatable({}, getmetatable(obj))&lt;br /&gt;
  s[obj] = res&lt;br /&gt;
  for k, v in pairs(obj) do res[p.copy(k, s)] = p.copy(v, s) end&lt;br /&gt;
  return res&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- get normalized label&lt;br /&gt;
function p.getDisplayLabel(jsondata, properties)&lt;br /&gt;
	local display_label = nil&lt;br /&gt;
	-- check if label properties are mapped&lt;br /&gt;
	if (properties[&amp;quot;HasLabel&amp;quot;] ~= nil and properties[&amp;quot;HasLabel&amp;quot;][1] ~= nil) then display_label = p.splitString(properties[&amp;quot;HasLabel&amp;quot;][1], &#039;@&#039;)[1] &lt;br /&gt;
	elseif (properties[&amp;quot;HasName&amp;quot;] ~= nil) then &lt;br /&gt;
		if type(properties[&amp;quot;HasName&amp;quot;]) == &#039;table&#039; then display_label = properties[&amp;quot;HasName&amp;quot;][1]&lt;br /&gt;
		else display_label = properties[&amp;quot;HasName&amp;quot;] end&lt;br /&gt;
	-- fall back to unmapped keywords&lt;br /&gt;
	elseif (jsondata[p.keys.label] ~= nil and jsondata[p.keys.label][1] ~= nil) then display_label = p.splitString(jsondata[p.keys.label][1], &#039;@&#039;)[1] &lt;br /&gt;
	elseif (jsondata[p.keys.name] ~= nil) then display_label = jsondata[p.keys.name] &lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return display_label&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- build normalized multilang label&lt;br /&gt;
function p.setNormalizedLabel(properties, use_fallbacks)&lt;br /&gt;
	if (use_fallbacks == nil) then use_fallbacks = true end&lt;br /&gt;
	if (properties[&#039;HasLabel&#039;] ~= nil) then &lt;br /&gt;
		labels = properties[&#039;HasLabel&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = p.splitString(label, &#039;@&#039;)[1]:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			label_lang = &amp;quot;en&amp;quot;&lt;br /&gt;
			if (p.splitString(label, &#039;@&#039;)[2] ~= nil) then label_lang = p.splitString(label, &#039;@&#039;)[2] end&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@&amp;quot; .. label_lang)	&lt;br /&gt;
		end&lt;br /&gt;
	&lt;br /&gt;
	elseif (use_fallbacks and properties[&#039;HasName&#039;] ~= nil) then -- fallback, assume English lang&lt;br /&gt;
		labels = properties[&#039;HasName&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@en&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	elseif (use_fallbacks and properties[&#039;Display title of&#039;] ~= nil) then -- fallback, assume English lang&lt;br /&gt;
		labels = properties[&#039;Display title of&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@en&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderMultilangValue(args)&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local key = p.defaultArg(args.key, &amp;quot;title&amp;quot;)&lt;br /&gt;
	local result = p.defaultArg(args.default, &amp;quot;&amp;quot;)&lt;br /&gt;
	local default = p.defaultArg(args.default, nil)&lt;br /&gt;
	-- &amp;quot;title*&amp;quot;: {&amp;quot;de&amp;quot;: ...}&lt;br /&gt;
	if jsonschema[key] ~= nil then result = jsonschema[key] end&lt;br /&gt;
	if jsonschema[key .. &#039;*&#039;] ~= nil then -- multilang label with switch&lt;br /&gt;
		result = &amp;quot;{{#switch:{{USERLANGUAGECODE}} |#default=&amp;quot; ..  result&lt;br /&gt;
		for k,v in pairs(jsonschema[key .. &#039;*&#039;]) do &lt;br /&gt;
			if k == en then default = v &lt;br /&gt;
			else result = result .. &amp;quot; |&amp;quot; .. k .. &amp;quot;=&amp;quot; .. v end &lt;br /&gt;
		end&lt;br /&gt;
		if default ~= nil then result = result .. &amp;quot; |#default=&amp;quot; ..  default end&lt;br /&gt;
		result = result .. &amp;quot; }}&amp;quot;&lt;br /&gt;
	end	&lt;br /&gt;
	-- &amp;quot;some_property&amp;quot;: [{&amp;quot;lang&amp;quot;: &amp;quot;de&amp;quot;, &amp;quot;text&amp;quot;: ...}]&lt;br /&gt;
	if jsondata[key] ~= nil then -- multilang label with switch&lt;br /&gt;
		result = &amp;quot;{{#switch:{{USERLANGUAGECODE}}&amp;quot;&lt;br /&gt;
		for k,v in pairs(jsondata[key]) do &lt;br /&gt;
			if v[&amp;quot;lang&amp;quot;] ~= nil and v[&amp;quot;text&amp;quot;] ~= nil then&lt;br /&gt;
				if v[&amp;quot;lang&amp;quot;] == &amp;quot;en&amp;quot; then default = v[&amp;quot;text&amp;quot;]&lt;br /&gt;
				else result = result .. &amp;quot; |&amp;quot; .. v[&amp;quot;lang&amp;quot;] .. &amp;quot;=&amp;quot; .. v[&amp;quot;text&amp;quot;] end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if default ~= nil then result = result .. &amp;quot; |#default=&amp;quot; ..  default end&lt;br /&gt;
		result = result .. &amp;quot; }}&amp;quot;&lt;br /&gt;
	end		&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Module:MwJson&amp;diff=4069</id>
		<title>Module:MwJson</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Module:MwJson&amp;diff=4069"/>
		<updated>2025-02-21T07:23:43Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Reverted edits by 0000-0003-0410-3616 (talk) to last revision by Maintenance script&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
&lt;br /&gt;
local lustache = require(&amp;quot;Module:Lustache&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local p = {} --p stands for package&lt;br /&gt;
&lt;br /&gt;
p.keys = { --jsonschema / json-ld keys&lt;br /&gt;
	category=&#039;type&#039;, &lt;br /&gt;
	category_pseudoproperty=&#039;Category&#039;, -- Property:Category&lt;br /&gt;
	subcategory=&#039;subclass_of&#039;,&lt;br /&gt;
	schema_type=&#039;schema_type&#039;,&lt;br /&gt;
	property_ns_prefix=&#039;Property&#039;,&lt;br /&gt;
	schema=&#039;osl_schema&#039;, &lt;br /&gt;
	template=&#039;eval_template&#039;,&lt;br /&gt;
	mode=&#039;mode&#039;,&lt;br /&gt;
	context=&#039;@context&#039;,&lt;br /&gt;
	allOf=&#039;allOf&#039;,&lt;br /&gt;
	label=&#039;label&#039;,&lt;br /&gt;
	name=&#039;name&#039;,&lt;br /&gt;
	description=&#039;description&#039;,&lt;br /&gt;
	text=&#039;text&#039;,&lt;br /&gt;
	debug=&#039;_debug&#039;&lt;br /&gt;
} &lt;br /&gt;
p.slots = { --slot names&lt;br /&gt;
	jsondata=&#039;jsondata&#039;, &lt;br /&gt;
	jsonschema=&#039;jsonschema&#039;, &lt;br /&gt;
	header_template=&#039;header_template&#039;,&lt;br /&gt;
	footer_template=&#039;footer_template&#039;,&lt;br /&gt;
	data_template=&#039;data_template&#039;&lt;br /&gt;
} &lt;br /&gt;
p.mode = {&lt;br /&gt;
	header=&#039;header&#039;,&lt;br /&gt;
	footer=&#039;footer&#039;,&lt;br /&gt;
	query=&#039;query&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
--loads json from a wiki page&lt;br /&gt;
--test: mw.logObject(p.loadJson({title=&amp;quot;JsonSchema:Entity&amp;quot;}))&lt;br /&gt;
--test: mw.logObject(p.loadJson({title=&amp;quot;Category:Entity&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}))&lt;br /&gt;
function p.loadJson(args)&lt;br /&gt;
	local page_title = p.defaultArg(args.title, &amp;quot;JsonSchema:Entity&amp;quot;) --for testing&lt;br /&gt;
	local slot = p.defaultArg(args.slot, nil)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, nil)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local json = {}&lt;br /&gt;
	&lt;br /&gt;
	if (slot == nil) then&lt;br /&gt;
		--json = mw.loadJsonData( &amp;quot;JsonSchema:Entity&amp;quot; ) --requires MediaWiki 1.39&lt;br /&gt;
		local page = mw.title.makeTitle(p.splitString(page_title, &#039;:&#039;)[1], p.splitString(page_title, &#039;:&#039;)[2])&lt;br /&gt;
		local text = page:getContent()&lt;br /&gt;
		if (text ~= nil) then json = mw.text.jsonDecode(text) end&lt;br /&gt;
	else&lt;br /&gt;
		if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. p.slots.jsondata .. &amp;quot; from page &amp;quot; .. title .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		local text = mw.slots.slotContent( slot , page_title )&lt;br /&gt;
		if (text ~= nil) then json = mw.text.jsonDecode(text) end&lt;br /&gt;
	end	&lt;br /&gt;
	&lt;br /&gt;
	--mw.logObject(json)&lt;br /&gt;
&lt;br /&gt;
	return {json=json, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- test: mw.logObject(p.walkJsonSchema({jsonschema=p.loadJson({title=&amp;quot;Category:Hardware&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema)&lt;br /&gt;
function p.walkJsonSchema(args)&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local jsonschemas = p.defaultArg(args.jsonschemas, {})&lt;br /&gt;
	local categories = p.defaultArg(args.categories, nil)&lt;br /&gt;
	local visited = p.defaultArg(args.visited, {})&lt;br /&gt;
	local mode = p.defaultArg(args.mode, p.mode.header)&lt;br /&gt;
	--local merged_jsonschema = p.defaultArg(args.merged_jsonschema, {})&lt;br /&gt;
	local templates = p.defaultArg(args.templates, {})&lt;br /&gt;
	local recursive = p.defaultArg(args.recursive, true)&lt;br /&gt;
	local root = p.defaultArg(args.root, true)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local category_template_slot = nil&lt;br /&gt;
	if (mode == p.mode.footer) then category_template_slot = p.slots.footer_template end&lt;br /&gt;
	if (mode == p.mode.header) then category_template_slot = p.slots.header_template end&lt;br /&gt;
	&lt;br /&gt;
	if (categories == nil) then categories = p.getCategories({jsonschema=jsonschema, includeNamespace=true}).categories end&lt;br /&gt;
	if (type(categories) ~= &#039;table&#039;) then categories = {categories} end&lt;br /&gt;
	if (debug) then msg = msg .. &amp;quot;Supercategories: &amp;quot; .. mw.dumpObject(categories) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	for k, category in pairs(categories) do&lt;br /&gt;
		if (not p.tableContains(visited, category)) then&lt;br /&gt;
			--mw.logObject(&amp;quot;Visit &amp;quot; .. category)&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. p.slots.jsonschema .. &amp;quot; from page &amp;quot; .. category .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			local super_jsonschema_str = mw.slots.slotContent( p.slots.jsonschema , category )&lt;br /&gt;
			if (super_jsonschema_str ~= nil) then&lt;br /&gt;
				super_jsonschema = mw.text.jsonDecode( super_jsonschema_str )&lt;br /&gt;
				if (recursive) then	&lt;br /&gt;
					local res = p.walkJsonSchema({jsonschema=super_jsonschema, jsonschemas=jsonschemas, templates=templates, mode=mode, visited=visited, root=false})&lt;br /&gt;
					wikitext = wikitext .. res.wikitext &lt;br /&gt;
				end&lt;br /&gt;
				--table.insert(jsonschemas, mw.text.jsonDecode( super_jsonschema_str )) --keep a copy of the schema, super_jsonschema passed by references gets modified&lt;br /&gt;
				--table.insert(jsonschemas, super_jsonschema ) &lt;br /&gt;
				--mw.logObject(&amp;quot;Store &amp;quot; .. category)&lt;br /&gt;
				table.insert(visited, category)&lt;br /&gt;
				jsonschemas[category] = mw.text.jsonDecode( super_jsonschema_str ) --keep a copy of the schema, super_jsonschema passed by references gets modified&lt;br /&gt;
				--jsonschema = p.tableMerge(jsonschema, super_jsonschema) --merge superschema is done by the caller&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. category_template_slot .. &amp;quot; from page &amp;quot; .. category .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			templates[category] = mw.slots.slotContent( category_template_slot , category )&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	if (root) then&lt;br /&gt;
		for i, category in ipairs(visited) do&lt;br /&gt;
			--merge all schemas. we need to make a copy here, otherwise jsonschemas[&amp;quot;Category:Entity&amp;quot;] contains the merged schema&lt;br /&gt;
			jsonschema = p.copy(p.tableMerge(jsonschema, jsonschemas[category])) &lt;br /&gt;
		end	&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then wikitext = msg .. wikitext  end&lt;br /&gt;
	return {jsonschema=jsonschema, jsonschemas=jsonschemas, templates=templates, visited=visited, wikitext=wikitext}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[ test: &lt;br /&gt;
category = &amp;quot;Category:Hardware&amp;quot;&lt;br /&gt;
page = &amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;&lt;br /&gt;
category2 = &amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;&lt;br /&gt;
page2 = &amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;&lt;br /&gt;
mw.logObject(p.expandEmbeddedTemplates({&lt;br /&gt;
	jsonschema=p.walkJsonSchema({jsonschema=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema, &lt;br /&gt;
	jsondata=p.loadJson({title=page, slot=&amp;quot;jsondata&amp;quot;}).json,&lt;br /&gt;
	debug=true, mode=&amp;quot;render&amp;quot;&lt;br /&gt;
}).res)&lt;br /&gt;
--]]&lt;br /&gt;
function p.expandEmbeddedTemplates(args)&lt;br /&gt;
	local frame = p.defaultArg(args.frame, mw.getCurrentFrame())&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local template = p.defaultArg(args.template, nil)&lt;br /&gt;
	local mode = p.defaultArg(args.mode, nil)&lt;br /&gt;
	local stringify_arrays = p.defaultArg(args.stringify_arrays, false)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	local res = p.defaultArg(args.jsondata, &amp;quot;&amp;quot;)&lt;br /&gt;
	&lt;br /&gt;
	for k,v in pairs(jsondata) do&lt;br /&gt;
		local eval_template = nil&lt;br /&gt;
		local eval_templates = p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, p.keys.template}, {})&lt;br /&gt;
		if (eval_templates[1] == nil) then eval_templates = {eval_templates} end --ensure list of objects&lt;br /&gt;
		for i, t in pairs(eval_templates) do&lt;br /&gt;
			if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then eval_template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
			elseif (t[p.keys.mode] == nil) then  eval_template = t --default&lt;br /&gt;
			elseif (debug) then msg = msg .. &amp;quot;Ignore eval_template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if (eval_template ~= nil and eval_template.value ~= nil and (eval_template.type == &amp;quot;mustache&amp;quot; or eval_template.type == &amp;quot;mustache-wikitext&amp;quot;)) then&lt;br /&gt;
			-- mustache can handle objects and array to we can parse it directly&lt;br /&gt;
			-- todo: handle nested templates&lt;br /&gt;
			local template_param = {[k]=v}&lt;br /&gt;
			if (eval_template.root_key == false) then template_param = v end&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Parse mustache template &amp;quot; .. eval_template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( template_param ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			jsondata[k] = lustache:render(eval_template.value, template_param, p.tableMerge({self=eval_template.value}, eval_template.partials)) -- render with self as registered partial for recursion&lt;br /&gt;
			if (eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then &lt;br /&gt;
				jsondata[k] = frame:preprocess( jsondata[k] )&lt;br /&gt;
			end&lt;br /&gt;
		elseif type(v) == &#039;table&#039; then &lt;br /&gt;
			if (v[1] == nil) then --key value array = object/dict&lt;br /&gt;
				local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=v, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays})&lt;br /&gt;
				msg = msg .. sub_res.debug_msg&lt;br /&gt;
				jsondata[k] = sub_res.res&lt;br /&gt;
				--if (sub_res.unparsed ~= nil) then jsondata[k] = sub_res.unparsed else jsondata[k] = sub_res.wikitext end&lt;br /&gt;
			else --list array&lt;br /&gt;
				local string_list = &amp;quot;&amp;quot;&lt;br /&gt;
				for i,e in pairs(v) do &lt;br /&gt;
					&lt;br /&gt;
					local eval_template = nil&lt;br /&gt;
					local eval_templates = p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;, p.keys.template}, {})&lt;br /&gt;
					if (eval_templates[1] == nil) then eval_templates = {eval_templates} end --ensure list of objects&lt;br /&gt;
					&lt;br /&gt;
					for i, t in pairs(eval_templates) do&lt;br /&gt;
						if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then eval_template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
						elseif (t[p.keys.mode] == nil) then  eval_template = t --default&lt;br /&gt;
						elseif (debug) then msg = msg .. &amp;quot;Ignore eval_template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					if type(e) == &#039;table&#039; then 	&lt;br /&gt;
						local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=e, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays})&lt;br /&gt;
						msg = msg .. sub_res.debug_msg&lt;br /&gt;
						if (type(sub_res.res) == &#039;table&#039;) then &lt;br /&gt;
							if (debug) then msg = msg .. &amp;quot;Values for &amp;quot; .. k .. &amp;quot; contains non-literal items: &amp;quot; .. mw.dumpObject( sub_res.res ) .. &amp;quot; =&amp;gt; skip value in wikitemplate array param creation\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
						else &lt;br /&gt;
							if (stringify_arrays) then string_list = string_list .. sub_res.res .. &amp;quot;;&amp;quot; &lt;br /&gt;
							else v[i] = sub_res.res end&lt;br /&gt;
						end&lt;br /&gt;
					else&lt;br /&gt;
						if (eval_template ~= nil and eval_template.value ~= nil) then&lt;br /&gt;
							&lt;br /&gt;
							--evaluate single array item string as json {&amp;quot;self&amp;quot;: &amp;quot;&amp;lt;value&amp;gt;&amp;quot;, &amp;quot;.&amp;quot;: &amp;quot;&amp;lt;value&amp;gt;&amp;quot;} =&amp;gt; does not work since jsondata is an object&lt;br /&gt;
							--e = p.expandEmbeddedTemplates({frame=frame, jsondata={[&amp;quot;self&amp;quot;]=e,[&amp;quot;.&amp;quot;]=e}, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays})&lt;br /&gt;
							&lt;br /&gt;
&lt;br /&gt;
							if (eval_template.type == &amp;quot;mustache&amp;quot; or eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then&lt;br /&gt;
								if (debug) then msg = msg .. &amp;quot;Parse mustache template &amp;quot; .. eval_template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( e ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
								-- {{.}} in the template will be the value of e&lt;br /&gt;
								e = lustache:render(eval_template.value, e, p.tableMerge({self=eval_template.value}, eval_template.partials)) -- render with self as registered partial for recursion&lt;br /&gt;
							end&lt;br /&gt;
							if (eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then --or eval_template.type == &amp;quot;wikitext&amp;quot;) then &lt;br /&gt;
								if (debug) then msg = msg .. &amp;quot;Parse wikitext template &amp;quot; .. e .. &amp;quot; with params &amp;quot; .. mw.dumpObject( e ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
								e = frame:preprocess( e )&lt;br /&gt;
							end&lt;br /&gt;
							v[i] = e -- update array&lt;br /&gt;
						end&lt;br /&gt;
						if (stringify_arrays) then string_list = string_list .. e .. &amp;quot;;&amp;quot; end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				if (stringify_arrays) then jsondata[k] = string_list end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	if (template == nil) then &lt;br /&gt;
		local templates = jsondata[p.keys.template]&lt;br /&gt;
		if (templates == nil) then templates = p.defaultArg(jsonschema[p.keys.template], {}) end&lt;br /&gt;
		if (templates[1] == nil) then templates = {templates} end --ensure list of objects&lt;br /&gt;
		for i, t in pairs(templates) do&lt;br /&gt;
			if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
			elseif (t[p.keys.mode] == nil) then  template = t --default&lt;br /&gt;
			elseif (debug) then msg = msg .. &amp;quot;Ignore template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if template ~= nil then&lt;br /&gt;
		if (template.type == &amp;quot;wikitext&amp;quot;) then&lt;br /&gt;
			for k,v in pairs(jsondata) do&lt;br /&gt;
				if type(v) == &#039;table&#039; then &lt;br /&gt;
					if (debug) then msg = msg .. &amp;quot;Values for &amp;quot; .. k .. &amp;quot; contains non-literals: &amp;quot; .. mw.dumpObject( v ) .. &amp;quot; =&amp;gt; skip wikitemplate parsing\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
					return {res=res, debug_msg=msg} &lt;br /&gt;
				end --not supported&lt;br /&gt;
			end			&lt;br /&gt;
			if (template.value ~= nil) then&lt;br /&gt;
				if (debug) then msg = msg .. &amp;quot;Parse wikitemplate &amp;quot; .. template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
				local child = frame:newChild{args=jsondata}&lt;br /&gt;
				res = child:preprocess( template.value )&lt;br /&gt;
			elseif (template.page ~= nil) then&lt;br /&gt;
				if (debug) then msg = msg .. &amp;quot;Parse wikitemplate &amp;quot; .. template.page .. &amp;quot; with params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
				res = frame:expandTemplate{ title = template.page, args = jsondata }&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--if (debug) then mw.logObject(msg) end&lt;br /&gt;
	return {res=res, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Category:OSWb3022bbf7e7146eb8e6f6e3264f50bbe&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;, categories={&amp;quot;Category:Category&amp;quot;}}))&lt;br /&gt;
function p.processJsondata(args)&lt;br /&gt;
	local frame = p.defaultArg(args.frame, mw.getCurrentFrame())&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local template = p.defaultArg(args.template, nil)&lt;br /&gt;
	local categories = p.defaultArg(args.categories, nil)&lt;br /&gt;
	local recursive = p.defaultArg(args.recursive, true)&lt;br /&gt;
	local mode = p.defaultArg(args.mode, p.mode.header)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local title = mw.title.getCurrentTitle()&lt;br /&gt;
	&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	local msg = &amp;quot;&amp;quot; --debug msg&lt;br /&gt;
&lt;br /&gt;
	if (p.nilOrEmpty(jsondata) or (p.nilOrEmpty(categories) and p.nilOrEmpty(jsonschema) and p.nilOrEmpty(jsondata[p.keys.category]))) then return {wikitext=wikitext, debug_msg=msg} end --nothing to do here&lt;br /&gt;
	--if (jsondata == nil or p.tableLength(jsondata) == 0 or (categories == nil and jsonschema == nil and jsondata[p.keys.category] == nil)) then return {wikitext=wikitext, debug_msg=msg} end --nothing to do here&lt;br /&gt;
	--jsonschema = p.defaultArg(jsonschema, {})&lt;br /&gt;
	--jsondata = p.defaultArg(jsondata, {})&lt;br /&gt;
	--if (categories == nil) then categories = jsondata[p.keys.category] end -- let function param overwrite json property&lt;br /&gt;
	if (not p.nilOrEmpty(jsondata[p.keys.category])) then categories = jsondata[p.keys.category] end -- let json property overwrite function param&lt;br /&gt;
	&lt;br /&gt;
	local schema_res = p.walkJsonSchema({jsonschema=jsonschema, categories=categories, mode=mode, recursive=recursive, debug=debug})&lt;br /&gt;
	local expand_res = p.expandJsonRef({json=schema_res.jsonschema, debug=debug})&lt;br /&gt;
	jsonschema = expand_res.json&lt;br /&gt;
	--mw.log(mw.text.jsonEncode(jsonschema))&lt;br /&gt;
	&lt;br /&gt;
	local display_label = p.defaultArgPath(jsondata, {p.keys.name}, &amp;quot;&amp;quot;)&lt;br /&gt;
	if (display_label == &amp;quot;&amp;quot; or (title.nsText ~= &amp;quot;Category&amp;quot; and title.nsText ~= &amp;quot;Property&amp;quot;)) then &lt;br /&gt;
		display_label = p.defaultArgPath(jsondata, {p.keys.label, 1, p.keys.text}, &amp;quot;&amp;quot;) --prefere label for all non-category and non-property pages&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	local jsonld = p.copy(jsondata)&lt;br /&gt;
	local json_data_store = p.copy(jsondata)&lt;br /&gt;
	local json_data_render = p.copy(jsondata)&lt;br /&gt;
	json_res_store = p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=json_data_store, mode=&#039;store&#039;})&lt;br /&gt;
	msg = msg .. json_res_store.debug_msg&lt;br /&gt;
	--mw.log(&amp;quot;JSONDATA STORE&amp;quot;)&lt;br /&gt;
	--mw.logObject(json_res_store.res)&lt;br /&gt;
	&lt;br /&gt;
	local smw_res = nil&lt;br /&gt;
	if (mode == p.mode.header) then&lt;br /&gt;
&lt;br /&gt;
		-- get the semantic properties by looking up the json keys in the json-ld context&lt;br /&gt;
		smw_res = p.getSemanticProperties({jsonschema=jsonschema, jsondata=json_res_store.res, store=false, debug=debug})&lt;br /&gt;
		&lt;br /&gt;
		-- store metadata where properties were defined / overridden&lt;br /&gt;
		for i, category in ipairs(schema_res.visited) do &lt;br /&gt;
			for k, v in pairs(schema_res.jsonschemas[category][&#039;properties&#039;]) do&lt;br /&gt;
				if smw_res.definitions[k] == nil then smw_res.definitions[k] = {} end&lt;br /&gt;
				if smw_res.definitions[k][&#039;defined_in&#039;] == nil then smw_res.definitions[k][&#039;defined_in&#039;] = {} end&lt;br /&gt;
				table.insert(smw_res.definitions[k][&#039;defined_in&#039;], category)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- embed json-ld in resulting html for search engine discovery&lt;br /&gt;
		jsonld[&amp;quot;@context&amp;quot;] = smw_res.context&lt;br /&gt;
		jsonld[&amp;quot;@type&amp;quot;] = p.tableMerge(p.tablefy(jsonschema.schema_type), p.tablefy(jsonld[&amp;quot;@type&amp;quot;])) --&lt;br /&gt;
		jsonld[&#039;schema:name&#039;] = p.defaultArgPath(jsonld, {p.keys.label, 1, p.keys.text}, jsonld[&#039;name&#039;]) --google does not support @value and @lang&lt;br /&gt;
		jsonld[&#039;schema:description&#039;] = p.defaultArgPath(jsonld, {p.keys.description, 1, p.keys.text}, nil)&lt;br /&gt;
		for k, v in pairs(jsonld) do&lt;br /&gt;
			if (type(v) == &amp;quot;string&amp;quot;) then&lt;br /&gt;
				local vpart = p.splitString(v, &#039;:&#039;)&lt;br /&gt;
				if (p.tableLength(vpart) == 2 and vpart[1] == &amp;quot;File&amp;quot;) then jsonld[k] = mw.getCurrentFrame():callParserFunction( &#039;filepath&#039;, { vpart[2] } ) end --google does not follow redirects via &amp;quot;File&amp;quot;:&amp;quot;wiki:Special:Redirect/file/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		wikitext = wikitext .. &amp;quot;&amp;lt;div class=&#039;jsonld-header&#039; style=&#039;display:none&#039; data-jsonld=&#039;&amp;quot; .. mw.text.jsonEncode( jsonld ):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;`&amp;quot;) .. &amp;quot;&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local json_res = p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=json_data_render, mode=&#039;render&#039;})&lt;br /&gt;
	msg = msg .. json_res.debug_msg&lt;br /&gt;
	jsondata =json_res.res&lt;br /&gt;
	--mw.log(&amp;quot;JSONDATA RENDER&amp;quot;)&lt;br /&gt;
	--mw.logObject(jsondata)&lt;br /&gt;
&lt;br /&gt;
	local max_index = p.tableLength(schema_res.visited)&lt;br /&gt;
	for i, category in ipairs(schema_res.visited) do&lt;br /&gt;
		if (mode == p.mode.footer) then category = schema_res.visited[max_index - i +1] end --reverse order for footer templates&lt;br /&gt;
		local super_jsonschema = schema_res.jsonschemas[category]&lt;br /&gt;
		local template = schema_res.templates[category]&lt;br /&gt;
		if (template ~= nil) then&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Parse \n\n&amp;quot; .. template .. &amp;quot; \n\nwith params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			local stripped_jsondata={}&lt;br /&gt;
			for k, v in pairs(jsondata) do&lt;br /&gt;
				if (type(v) ~= &#039;table&#039;) then stripped_jsondata[k] = v end --delete object values, not supported by wiki templates	&lt;br /&gt;
			end&lt;br /&gt;
			local child = frame:newChild{args=stripped_jsondata}&lt;br /&gt;
			if ( template:sub(1, #&amp;quot;=&amp;quot;) == &amp;quot;=&amp;quot; ) then template = &amp;quot;\n&amp;quot; .. template end -- add line break if template starts with heading (otherwise not rendered by mw parser)&lt;br /&gt;
			wikitext = wikitext .. child:preprocess( template )&lt;br /&gt;
		elseif (mode == p.mode.header) then&lt;br /&gt;
			local ignore_properties = {[p.keys.category]=true} -- don&#039;t render type/category on every subclass&lt;br /&gt;
			for j, subcategory in ipairs(schema_res.visited) do&lt;br /&gt;
				if j &amp;gt; i then&lt;br /&gt;
					local subjsonschema = schema_res.jsonschemas[subcategory]&lt;br /&gt;
					for k, v in pairs(subjsonschema[&#039;properties&#039;]) do&lt;br /&gt;
						-- skip properties that are overwritten in subschemas, render them only once at the most specific position&lt;br /&gt;
						ignore_properties[k] = true&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			-- render the infobox for the schema itself and every super_schema using always the global json-ld context (merged within walkJsonSchema())&lt;br /&gt;
			-- context needs to be preprocessed with buildContext() since the generic json/table merge of the @context atttribute produces a list of strings (remote context) and context objects&lt;br /&gt;
			-- context is already build in p.getSemanticProperties. schema_allOfMerged is used to provide the full schema for overridden properties&lt;br /&gt;
			local infobox_res = p.renderInfoBox({jsonschema=super_jsonschema, schema_allOfMerged=jsonschema, context=smw_res.context, property_definitions=smw_res.definitions, jsondata=jsondata, ignore_properties=ignore_properties})&lt;br /&gt;
			wikitext = wikitext .. frame:preprocess( infobox_res.wikitext )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--local display_label = &amp;quot;&amp;quot;&lt;br /&gt;
	--if (jsondata[p.keys.label] ~= nil) then display_label = p.splitString(jsondata[p.keys.label], &#039;@&#039;)[1] end&lt;br /&gt;
	local set_categories_in_wikitext = {}&lt;br /&gt;
	p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.subcategory])  --classes/categories, nil for items&lt;br /&gt;
	if (title.nsText ~= &amp;quot;Category&amp;quot;) then --items&lt;br /&gt;
		p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.category]) -- categories from schema type&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Todo: Consider moving the category and this block to p.getSemanticProperties with store=true. However, settings categories with @category is only possible for subobjects&lt;br /&gt;
	if (smw_res ~= nil) then&lt;br /&gt;
		if (debug) then msg = msg .. &amp;quot;Store page properties&amp;quot; end&lt;br /&gt;
		-- category handling&lt;br /&gt;
		p.tableMerge(set_categories_in_wikitext, smw_res.properties[p.keys.category_pseudoproperty]) &lt;br /&gt;
		smw_res.properties[p.keys.category_pseudoproperty] = nil -- delete pseudo property&lt;br /&gt;
		&lt;br /&gt;
		smw_res.properties[&#039;HasOswId&#039;] = mw.title.getCurrentTitle().fullText  --set special property OswId to own title&lt;br /&gt;
		&lt;br /&gt;
		-- label and display title handling&lt;br /&gt;
		smw_res.properties[&#039;Display title of&#039;] = display_label --set special property display title&lt;br /&gt;
		smw_res.properties[&#039;Display title of lowercase&#039;] = display_label:lower() --store lowercase for case insensitive query&lt;br /&gt;
		smw_res.properties[&#039;Display title of normalized&#039;] = display_label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;) --store with all non-alphanumeric chars removed for normalized query&lt;br /&gt;
		p.setNormalizedLabel(smw_res.properties) --build normalized multilang label&lt;br /&gt;
		mw.ext.displaytitle.set(display_label)&lt;br /&gt;
		--smw_res.properties[&#039;@category&#039;] = jsondata[p.keys.category]&lt;br /&gt;
		local store_res = mw.smw.set( smw_res.properties ) --store as semantic properties&lt;br /&gt;
		if (debug) then msg = msg .. mw.dumpObject(smw_res.properties) end&lt;br /&gt;
		if (store_res) then &lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;SMW SUCCESS: &amp;quot; end&lt;br /&gt;
		else&lt;br /&gt;
			wikitext = wikitext .. store_res.error &lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;SMW ERROR: &amp;quot; .. store_res.error end&lt;br /&gt;
		end&lt;br /&gt;
		--wikitext = mw.dumpObject(smw_res.properties) .. wikitext&lt;br /&gt;
	end&lt;br /&gt;
	wikitext = wikitext .. &amp;quot;\n&amp;quot; .. p.setCategories({categories=set_categories_in_wikitext, sortkey=display_label}).wikitext&lt;br /&gt;
	&lt;br /&gt;
	if (debug) then mw.logObject(res) end&lt;br /&gt;
	return {wikitext=wikitext, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- renders a default infobox&lt;br /&gt;
-- test: mw.logObject(p.renderInfoBox({jsonschema=p.loadJson({title=&amp;quot;JsonSchema:Entity&amp;quot;}).json, jsondata={uuid=&amp;quot;123123&amp;quot;}}))&lt;br /&gt;
function p.renderInfoBox(args)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, nil) -- local schema from the perspective of the current category&lt;br /&gt;
	local schema_allOfMerged = p.defaultArg(args.schema_allOfMerged, schema) -- global schema with allOfs merged&lt;br /&gt;
	local property_definitions = p.defaultArg(args.property_definitions, {}) -- dict schema_key: {property: &amp;lt;smw_property&amp;gt;, ...}&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	if schema == nil then return res end&lt;br /&gt;
	&lt;br /&gt;
	local context = p.defaultArg(args.context, p.buildContext({jsonschema=schema}).context)&lt;br /&gt;
	local ignore_properties = p.defaultArg(args.ignore_properties, {})&lt;br /&gt;
&lt;br /&gt;
	local schema_label = &amp;quot;&amp;quot;&lt;br /&gt;
	if schema[&#039;title&#039;] ~= nil then schema_label = schema[&#039;title&#039;] end&lt;br /&gt;
	&lt;br /&gt;
	-- see also: https://help.fandom.com/wiki/Extension:Scribunto/HTML_Library_usage_notes&lt;br /&gt;
	local tbl = mw.html.create( &#039;table&#039; )&lt;br /&gt;
	tbl&lt;br /&gt;
		:attr( &#039;class&#039;, &#039;info_box&#039; )&lt;br /&gt;
		:tag( &#039;tr&#039; )&lt;br /&gt;
			:tag( &#039;th&#039; )&lt;br /&gt;
				:attr( &#039;class&#039;, &#039;heading&#039; )&lt;br /&gt;
				:attr( &#039;colspan&#039;, &#039;2&#039; )&lt;br /&gt;
				:wikitext( schema_label )&lt;br /&gt;
	for k,v in pairs(jsondata) do&lt;br /&gt;
		if (not ignore_properties[k]) then&lt;br /&gt;
			if (schema[&#039;properties&#039;] ~= nil and schema[&#039;properties&#039;][k] ~= nil and (type(v) ~= &#039;table&#039; or v[1] ~= nil)) then --literal or literal array&lt;br /&gt;
				local def = schema_allOfMerged[&#039;properties&#039;][k]&lt;br /&gt;
				--mw.logObject(def)&lt;br /&gt;
				&lt;br /&gt;
				local label = k&lt;br /&gt;
				if def[&#039;title&#039;] ~= nil then label = def[&#039;title&#039;] end&lt;br /&gt;
				if def[&#039;title*&#039;] ~= nil then -- multilang label with switch&lt;br /&gt;
					label = &amp;quot;{{#switch:{{USERLANGUAGECODE}} |#default=&amp;quot; ..  label&lt;br /&gt;
					for k,v in pairs(def[&#039;title*&#039;]) do label = label .. &amp;quot; |&amp;quot; .. k .. &amp;quot;=&amp;quot; .. v end&lt;br /&gt;
					label = label .. &amp;quot; }}&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
				&lt;br /&gt;
				local description = &amp;quot;&amp;quot;&lt;br /&gt;
				if def[&#039;description&#039;] ~= nil then description = def[&#039;description&#039;] end&lt;br /&gt;
				if def[&#039;description*&#039;] ~= nil then -- multilang label with switch&lt;br /&gt;
					description = &amp;quot;{{#switch:{{USERLANGUAGECODE}} |#default=&amp;quot; ..  description&lt;br /&gt;
					for k,v in pairs(def[&#039;description*&#039;]) do description = description .. &amp;quot; |&amp;quot; .. k .. &amp;quot;=&amp;quot; .. v end&lt;br /&gt;
					description = description .. &amp;quot; }}&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
				if (p.tableLength(p.defaultArgPath(property_definitions, {k, &#039;defined_in&#039;}, {})) &amp;gt; 0) then description = description .. &amp;quot;&amp;lt;br&amp;gt;Definition: &amp;quot; end&lt;br /&gt;
				for i, c in pairs(p.defaultArgPath(property_definitions, {k, &#039;defined_in&#039;}, {})) do &lt;br /&gt;
					if (i &amp;gt; 1) then description = description .. &amp;quot;, &amp;quot; end&lt;br /&gt;
					description = description .. &amp;quot;[[:&amp;quot; ..c .. &amp;quot;]]&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
				if (description ~= &amp;quot;&amp;quot;) then description = &amp;quot;{{#info: &amp;quot; .. description .. &amp;quot;|note }}&amp;quot; end -- smw tooltip&lt;br /&gt;
				label = label .. description&lt;br /&gt;
&lt;br /&gt;
				--res = res .. title &amp;quot;: &amp;quot; .. v&lt;br /&gt;
				local cell = tbl:tag( &#039;tr&#039; )&lt;br /&gt;
									:tag( &#039;th&#039; )&lt;br /&gt;
										:wikitext( label )&lt;br /&gt;
										:done()&lt;br /&gt;
									:tag( &#039;td&#039; )&lt;br /&gt;
				if (type(v) == &#039;table&#039;) then&lt;br /&gt;
					for i,e in pairs(v) do &lt;br /&gt;
						if (type(e) ~= &#039;table&#039;) then &lt;br /&gt;
							local p_type = p.defaultArgPath(context, {k, &#039;@type&#039;}, &#039;@value&#039;)&lt;br /&gt;
							if (p_type == &#039;@id&#039; and p.defaultArgPath(def, {&#039;items&#039;, &#039;type&#039;}, &#039;unknown&#039;) == &#039;string&#039; and def[&#039;eval_template&#039;] == nil) then&lt;br /&gt;
								-- auto-link (OSW-)IDs if no eval_template is present&lt;br /&gt;
								e = string.gsub(e, &amp;quot;Category:&amp;quot;, &amp;quot;:Category:&amp;quot;) -- make sure category links work&lt;br /&gt;
								e = string.gsub(e, &amp;quot;File:&amp;quot;, &amp;quot;:File:&amp;quot;) -- do not embedd images but link to them&lt;br /&gt;
								e = &amp;quot;[[&amp;quot; .. e .. &amp;quot;]]&amp;quot; &lt;br /&gt;
							elseif (p_type == &#039;xsd:date&#039;) then -- formate date with user preferences&lt;br /&gt;
								e = &amp;quot;{{#dateformat:&amp;quot; .. e .. &amp;quot;|ymd}}&amp;quot; &lt;br /&gt;
							elseif (p_type == &#039;xsd:dateTime&#039;) then -- formate time with user preferences&lt;br /&gt;
								local smw_property = p.defaultArgPath(property_definitions, {k, &#039;property&#039;})&lt;br /&gt;
								if (smw_property ~= nil) then e = &amp;quot;{{#ask: [[{{FULLPAGENAME}}]]|?&amp;quot; .. smw_property .. &amp;quot;#LOCL#TO= |format=plain |mainlabel=-}}&amp;quot;&lt;br /&gt;
								else &lt;br /&gt;
									local _, _, date, hours, minutes = string.find(e, &amp;quot;(%S+)[T ](%S+)[:](%S+)[:?]&amp;quot;)&lt;br /&gt;
									e = &amp;quot;{{#dateformat:&amp;quot; .. date .. &amp;quot;|ymd}} &amp;quot; .. hours .. &amp;quot;:&amp;quot; .. minutes .. &amp;quot; (UTC)&amp;quot;&lt;br /&gt;
								end&lt;br /&gt;
							elseif (type(v) == &#039;boolean&#039;) then &lt;br /&gt;
								if (v) then v = &amp;quot;&amp;amp;#x2705;&amp;quot; else v = &amp;quot;&amp;amp;#x274C;&amp;quot; end -- green check mark or red cross&lt;br /&gt;
							elseif ((string.len(e) &amp;gt; 100) and (string.find(e, &amp;quot;{{&amp;quot;) == nil) and (string.find(e, &amp;quot;&amp;lt;/&amp;quot;) == nil)) then &lt;br /&gt;
								e = string.sub(e, 1, 100) .. &amp;quot;...&amp;quot;; -- limit infobox plain text to max 100 chars&lt;br /&gt;
							elseif (debug) then&lt;br /&gt;
								mw.log(&amp;quot;Unformated: &amp;quot; .. k .. &amp;quot; &amp;quot; .. p.defaultArgPath(def, {&#039;items&#039;, &#039;type&#039;}, &#039;unknown&#039;))&lt;br /&gt;
								mw.logObject(def)&lt;br /&gt;
							end&lt;br /&gt;
							cell:wikitext(&amp;quot;\n* &amp;quot; .. e .. &amp;quot;&amp;quot;) &lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					local p_type = p.defaultArgPath(context, {k, &#039;@type&#039;}, &#039;@value&#039;)&lt;br /&gt;
					if (p_type == &#039;@id&#039; and p.defaultArgPath(def, {&#039;type&#039;}, &#039;unknown&#039;) == &#039;string&#039; and def[&#039;eval_template&#039;] == nil) then &lt;br /&gt;
						-- auto-link (OSW-)IDs if no eval_template is present&lt;br /&gt;
						v = string.gsub(v, &amp;quot;Category:&amp;quot;, &amp;quot;:Category:&amp;quot;) -- make sure category links work&lt;br /&gt;
						v = string.gsub(v, &amp;quot;File:&amp;quot;, &amp;quot;:File:&amp;quot;) -- do not embedd images but link to them&lt;br /&gt;
						v = &amp;quot;[[&amp;quot; .. v .. &amp;quot;]]&amp;quot; &lt;br /&gt;
					elseif (p_type == &#039;xsd:date&#039;) then -- formate date &amp;amp; time with user preferences&lt;br /&gt;
						v = &amp;quot;{{#dateformat:&amp;quot; .. v .. &amp;quot;|ymd}}&amp;quot;&lt;br /&gt;
					elseif (p_type == &#039;xsd:dateTime&#039;) then -- formate time with user preferences&lt;br /&gt;
						local smw_property = p.defaultArgPath(property_definitions, {k, &#039;property&#039;})&lt;br /&gt;
						if (smw_property ~= nil) then v = &amp;quot;{{#ask: [[{{FULLPAGENAME}}]]|?&amp;quot; .. smw_property .. &amp;quot;#LOCL#TO= |format=plain |mainlabel=-}}&amp;quot;&lt;br /&gt;
						else &lt;br /&gt;
							local _, _, date, hours, minutes = string.find(v, &amp;quot;(%S+)[T ](%S+)[:](%S+)[:?]&amp;quot;)&lt;br /&gt;
							v = &amp;quot;{{#dateformat:&amp;quot; .. date .. &amp;quot;|ymd}} &amp;quot; .. hours .. &amp;quot;:&amp;quot; .. minutes .. &amp;quot; (UTC)&amp;quot;&lt;br /&gt;
						end&lt;br /&gt;
					elseif (type(v) == &#039;boolean&#039;) then &lt;br /&gt;
						if (v) then v = &amp;quot;&amp;amp;#x2705;&amp;quot; else v = &amp;quot;&amp;amp;#x274C;&amp;quot; end -- green check mark or red cross&lt;br /&gt;
					elseif ((string.len(v) &amp;gt; 100) and (string.find(v, &amp;quot;{{&amp;quot;) == nil) and (string.find(v, &amp;quot;&amp;lt;/&amp;quot;) == nil)) then&lt;br /&gt;
						v = string.sub(v, 1, 100) .. &amp;quot;...&amp;quot;; -- limit infobox plain text to max 100 chars&lt;br /&gt;
					elseif (debug) then&lt;br /&gt;
						mw.log(&amp;quot;Unformated: &amp;quot; .. k .. &amp;quot; &amp;quot; .. p.defaultArgPath(def, {&#039;type&#039;}, &#039;unknown&#039;))&lt;br /&gt;
						mw.logObject(def)&lt;br /&gt;
					end&lt;br /&gt;
					cell:wikitext(&amp;quot;\n&amp;quot; .. v .. &amp;quot;&amp;quot;)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	res = res .. tostring( tbl )&lt;br /&gt;
	--mw.logObject(res)&lt;br /&gt;
	&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- test&lt;br /&gt;
-- mw.logObject(p.getCategories({jsonschema={allOf={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test?action=raw&amp;amp;slot=jsonschema&amp;quot;}}, includeNamespace=true}))&lt;br /&gt;
-- mw.logObject(p.getCategories({jsonschema={allOf={{[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test?action=raw&amp;amp;slot=jsonschema&amp;quot;}, {[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test2?action=raw&amp;amp;slot=jsonschema&amp;quot;}}}}))&lt;br /&gt;
function p.getCategories(args)&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local includeNamespace = p.defaultArg(args.includeNamespace, false)&lt;br /&gt;
	&lt;br /&gt;
	local categories = {}&lt;br /&gt;
		local allOf = jsonschema[p.keys.allOf]&lt;br /&gt;
		if (allOf ~= nil) then&lt;br /&gt;
			--properties[&#039;@category&#039;] = {}&lt;br /&gt;
			for k, entry in pairs(allOf) do&lt;br /&gt;
				if type(entry) == &#039;table&#039; then -- &amp;quot;allOf&amp;quot;: [{&amp;quot;$ref&amp;quot;: &amp;quot;/wiki/Category:Test?action=raw&amp;quot;}]&lt;br /&gt;
					for p, v in pairs(entry) do&lt;br /&gt;
						if (p == &#039;$ref&#039;) then&lt;br /&gt;
							for category in v:gmatch(&amp;quot;Category:([^?]+)&amp;quot;) do -- e.g. &amp;quot;/wiki/Category:Test?action=raw&amp;quot;&lt;br /&gt;
								if (includeNamespace) then category = &amp;quot;Category:&amp;quot; .. category end&lt;br /&gt;
							    table.insert(categories, category)&lt;br /&gt;
							end&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				else -- &amp;quot;allOf&amp;quot;: {&amp;quot;$ref&amp;quot;: &amp;quot;/wiki/Category:Test?action=raw&amp;quot;}&lt;br /&gt;
					if (k == &#039;$ref&#039;) then&lt;br /&gt;
						for category in entry:gmatch(&amp;quot;Category:([^?]+)&amp;quot;) do -- e.g. &amp;quot;/wiki/Category:Test?action=raw&amp;quot;&lt;br /&gt;
							if (includeNamespace) then category = &amp;quot;Category:&amp;quot; .. category end&lt;br /&gt;
							table.insert(categories, category)&lt;br /&gt;
						end&lt;br /&gt;
					end	&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end	&lt;br /&gt;
		&lt;br /&gt;
	return {categories=categories}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--sets a list of categories on the page&lt;br /&gt;
--test: mw.logObject(p.setCategories({categories={&amp;quot;Cat1&amp;quot;, &amp;quot;Category:Cat2&amp;quot;}}))&lt;br /&gt;
function p.setCategories(args)&lt;br /&gt;
	local categories = p.defaultArg(args.categories, {})&lt;br /&gt;
	local sortkey = p.defaultArg(args.sortkey, &amp;quot;&amp;quot;)&lt;br /&gt;
	if (sortkey ~= &amp;quot;&amp;quot;) then sortkey = &amp;quot;|&amp;quot; .. sortkey end&lt;br /&gt;
	if (type(categories) ~= &#039;table&#039;) then categories = {categories} end&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	for k, entry in pairs(categories) do&lt;br /&gt;
		res = res .. &amp;quot;[[Category:&amp;quot; .. string.gsub(entry, &amp;quot;Category:&amp;quot;, &amp;quot;&amp;quot;) .. sortkey ..&amp;quot;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[ test&lt;br /&gt;
category = &amp;quot;Category:Entity&amp;quot;&lt;br /&gt;
jsonschema = p.expandJsonRef({json=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json}).json&lt;br /&gt;
mw.logObject(p.buildContext({jsonschema=jsonschema, debug=true}))&lt;br /&gt;
mw.log(mw.text.jsonEncode(p.buildContext({jsonschema=jsonschema, debug=false}).context))&lt;br /&gt;
or&lt;br /&gt;
jsonschema = {&lt;br /&gt;
	[&amp;quot;@context&amp;quot;]={test=&amp;quot;level 0&amp;quot;}, &lt;br /&gt;
	properties={&lt;br /&gt;
		test={&lt;br /&gt;
			type=&amp;quot;object&amp;quot;,&lt;br /&gt;
			[&amp;quot;@context&amp;quot;]={test1=&amp;quot;level 1&amp;quot;}, &lt;br /&gt;
			properties= {&lt;br /&gt;
				test= {&lt;br /&gt;
					type=&amp;quot;array&amp;quot;,&lt;br /&gt;
					items= {&lt;br /&gt;
						type=&amp;quot;object&amp;quot;,&lt;br /&gt;
						[&amp;quot;@context&amp;quot;]={test2=&amp;quot;level 2&amp;quot;}&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
mw.logObject(p.buildContext({jsonschema=jsonschema, debug=true}))&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- constructs a property specific local jsonld context&lt;br /&gt;
function p.buildContext(args)&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	--mw.logObject(schema)&lt;br /&gt;
	local context = p.defaultArg(args.context, schema[p.keys.context])&lt;br /&gt;
	local result = p.defaultArg(args.result, {})&lt;br /&gt;
	if (context ~= nil) then&lt;br /&gt;
		for k,v in pairs(context) do&lt;br /&gt;
			if type(k) == &#039;number&#039; and type(v) == &#039;string&#039; then&lt;br /&gt;
				--table.insert(result, v) --skip context imports&lt;br /&gt;
			elseif (type(v) == &#039;table&#039; and v[1] ~= nil) then --custom addtional mappings, e. g. &amp;quot;type*&amp;quot;: [&amp;quot;Property:HasType&amp;quot;]&lt;br /&gt;
				result[k] = v&lt;br /&gt;
			elseif (type(v) == &#039;table&#039; and v[&#039;@id&#039;] == nil and v[&#039;@reverse&#039;] == nil) then --subcontext&lt;br /&gt;
				p.tableMerge(result, p.buildContext({context=v}).context)&lt;br /&gt;
			else &lt;br /&gt;
				result[k] = v	&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local properties = p.defaultArg(schema.properties, {})&lt;br /&gt;
&lt;br /&gt;
	-- build property context&lt;br /&gt;
	for k,v in pairs(properties) do&lt;br /&gt;
		local subcontext = nil&lt;br /&gt;
		if (p.defaultArgPath(properties, {k, &#039;type&#039;}) == &#039;object&#039;) then&lt;br /&gt;
			--mw.logObject(properties[k])&lt;br /&gt;
			subcontext = p.buildContext({jsonschema=properties[k]}).context&lt;br /&gt;
		elseif (p.defaultArgPath(properties, {k, &#039;items&#039;, &#039;type&#039;}) == &#039;object&#039;) then &lt;br /&gt;
			--mw.logObject(properties[k][&#039;items&#039;])&lt;br /&gt;
			subcontext = p.buildContext({jsonschema=properties[k][&#039;items&#039;]}).context&lt;br /&gt;
		end&lt;br /&gt;
		if (subcontext ~= nil and p.tableLength(subcontext) &amp;gt; 0) then&lt;br /&gt;
			if (result[k] == nil) then result[k] = {} end&lt;br /&gt;
			if (type(result[k]) == &#039;string&#039;) then result[k] = {[&amp;quot;@id&amp;quot;]=result[k]} end&lt;br /&gt;
			if (result[k][p.keys.context] == nil) then result[k][p.keys.context] = {} end&lt;br /&gt;
			result[k][p.keys.context] = p.tableMerge(result[k][p.keys.context], subcontext)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return {context=result}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--maps jsondata values to semantic properties by using the @context attribute within the schema&lt;br /&gt;
--test: mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={test=&amp;quot;Property:schema:TestProperty&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}, jsondata={test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;}, debug=true}))&lt;br /&gt;
--test: mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={&amp;quot;some uri&amp;quot;,{test=&amp;quot;Property:TestProperty&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}}, jsondata={test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;}, debug=true}))&lt;br /&gt;
--[[&lt;br /&gt;
mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={test=&amp;quot;Property:TestProperty&amp;quot;, subobject=&amp;quot;Property:HasSubobject&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}, jsondata={&lt;br /&gt;
test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;, subobject={uuid=&amp;quot;123-123-123&amp;quot;, test=&amp;quot;TestValue2&amp;quot;}&lt;br /&gt;
}, debug=true}))&lt;br /&gt;
&lt;br /&gt;
mw.logObject(p.getSemanticProperties({jsonschema=p.loadJson({title=&amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}).json, p.loadJson({title=&amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;, slot=&amp;quot;jsonsdata&amp;quot;}).json, debug=true}))&lt;br /&gt;
&lt;br /&gt;
category = &amp;quot;Category:Hardware&amp;quot;&lt;br /&gt;
page = &amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;&lt;br /&gt;
category2 = &amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;&lt;br /&gt;
page2 = &amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;&lt;br /&gt;
jsonschema =p.walkJsonSchema({jsonschema=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema&lt;br /&gt;
mw.logObject(p.getSemanticProperties({&lt;br /&gt;
	jsonschema=jsonschema,&lt;br /&gt;
	jsondata=p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=p.loadJson({title=page, slot=&amp;quot;jsondata&amp;quot;}).json}).res,&lt;br /&gt;
	debug=true&lt;br /&gt;
}).properties)&lt;br /&gt;
&lt;br /&gt;
--]]&lt;br /&gt;
function p.getSemanticProperties(args)&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local subschema = p.defaultArg(args.subschema, schema)&lt;br /&gt;
	local parent_schema_property = p.defaultArg(args.parent_schema_property, {}) -- ToDo: Not used except in getSemanticQuery =&amp;gt; remove&lt;br /&gt;
	local store = p.defaultArg(args.store, false)&lt;br /&gt;
	local root = p.defaultArg(args.root, true)&lt;br /&gt;
	local properties = p.defaultArg(args.properties, {}) --semantic properties to store, dict key=property_name, value=array of string values&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	--if (debug) then mw.logObject(&amp;quot;Call getSemanticProperties with args &amp;quot; .. mw.dumpObject( args ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;) end&lt;br /&gt;
	&lt;br /&gt;
	local subjectId = mw.title.getCurrentTitle().fullText&lt;br /&gt;
	local subobjectId = nil&lt;br /&gt;
	if (root == false and jsondata[&#039;uuid&#039;] ~= nil) then &lt;br /&gt;
		subobjectId = &amp;quot;OSW&amp;quot; .. string.gsub(jsondata[&#039;uuid&#039;], &amp;quot;-&amp;quot;, &amp;quot;&amp;quot;) &lt;br /&gt;
		subjectId = subjectId .. &#039;#&#039; .. subobjectId&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local property_data = {}&lt;br /&gt;
	local context = p.defaultArg(args.context, p.buildContext({jsonschema=schema}).context)&lt;br /&gt;
	local error = &amp;quot;&amp;quot;&lt;br /&gt;
	if (debug) then mw.logObject(context) end&lt;br /&gt;
	if schema ~= nil and context ~= nil then&lt;br /&gt;
		local schema_properties = p.defaultArg(subschema.properties, {})&lt;br /&gt;
		if (debug and root) then&lt;br /&gt;
			for k,v in pairs(context) do&lt;br /&gt;
				if type(k) == &#039;number&#039; then mw.logObject(&amp;quot;imports &amp;quot; .. v)&lt;br /&gt;
				elseif type(v) == &#039;table&#039; and v[&amp;quot;@id&amp;quot;] ~= nil then mw.logObject(&amp;quot;&amp;quot; .. k .. &amp;quot; maps to &amp;quot; .. v[&amp;quot;@id&amp;quot;]) &lt;br /&gt;
				else mw.logObject(&amp;quot;&amp;quot; .. k .. &amp;quot; maps to &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		for k,v in pairs(jsondata) do&lt;br /&gt;
			local property_names = {}&lt;br /&gt;
			local subobject_properties = {} -- reverse properties to store in the subobject&lt;br /&gt;
			local mapping_found = false&lt;br /&gt;
			local property_definitions = {} -- list of objects {id=..., reverse=...}&lt;br /&gt;
&lt;br /&gt;
			for term, def in pairs(context) do&lt;br /&gt;
				local term_parts = p.splitString(term, &amp;quot;*&amp;quot;)&lt;br /&gt;
				if (term_parts[1] == k) then --custom additional mapping term*(*...): &amp;quot;Property:...&amp;quot;&lt;br /&gt;
					if type(def) == &#039;table&#039; then &lt;br /&gt;
						-- note: json-ld allows only @id OR @reverse&lt;br /&gt;
						if (def[&amp;quot;@id&amp;quot;] ~= nil) then table.insert(property_definitions, {id=def[&amp;quot;@id&amp;quot;], reverse=false}) end&lt;br /&gt;
						if (def[&amp;quot;@reverse&amp;quot;] ~= nil) then table.insert(property_definitions, {id=def[&amp;quot;@reverse&amp;quot;], reverse=true}) end&lt;br /&gt;
					else table.insert(property_definitions, {id=def}) end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if (debug) then mw.logObject(property_definitions) end&lt;br /&gt;
			for i,e in ipairs(property_definitions) do &lt;br /&gt;
				local id = e[&amp;quot;id&amp;quot;]&lt;br /&gt;
				local property_definition = p.splitString(id, &#039;:&#039;)&lt;br /&gt;
				if property_definition[1] == p.keys.property_ns_prefix then&lt;br /&gt;
					mapping_found = true&lt;br /&gt;
					property_name = string.gsub(id, p.keys.property_ns_prefix .. &amp;quot;:&amp;quot;, &amp;quot;&amp;quot;) -- also allow prefix properties like: Property:schema:url&lt;br /&gt;
					if (e[&amp;quot;reverse&amp;quot;]) then -- reverse properties are handled in the respective subobject&lt;br /&gt;
						if (subobject_properties[property_name] == nil) then subobject_properties[property_name] = {} end --initialize empty list&lt;br /&gt;
						table.insert(subobject_properties[property_name], subjectId) -- add triple subobject -property-&amp;gt; subject&lt;br /&gt;
					else table.insert(property_names, property_name) end&lt;br /&gt;
					local schema_property = p.defaultArg(schema_properties[k], {})&lt;br /&gt;
					local schema_type = p.defaultArg(schema_property.type, nil) --todo: also load smw property type on demand&lt;br /&gt;
					property_data[k] = {schema_type=schema_type, schema_data=schema_property, property=property_name, value=v, reverse=e[&amp;quot;reverse&amp;quot;]}&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			for i, property_name in ipairs(property_names) do&lt;br /&gt;
				if (properties[property_name] == nil) then properties[property_name] = {} end --initialize empty list&lt;br /&gt;
			end&lt;br /&gt;
			if type(v) == &#039;table&#039; then &lt;br /&gt;
				--if (debug) then mw.logObject(&amp;quot;prop &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
				if (mapping_found) then&lt;br /&gt;
					local subcontext = p.copy(p.defaultArgPath(context, {k, p.keys.context}, {})) --deepcopy, see also https://phabricator.wikimedia.org/T269990&lt;br /&gt;
					context = p.tableMerge(context, subcontext) -- pull up nested context&lt;br /&gt;
					local values = {}&lt;br /&gt;
					if (v[1] == nil) then --key value array = object/dict&lt;br /&gt;
						local subproperties_res = p.getSemanticProperties({jsonschema=schema, jsondata=v, properties=p.copy(subobject_properties), store=true, root=false, debug=debug, context=context, subschema=schema_properties[k], parent_schema_property=property_data[k]})&lt;br /&gt;
						local id = subproperties_res.id --subobject_id&lt;br /&gt;
						if (id ~= nil) then &lt;br /&gt;
							id = mw.title.getCurrentTitle().fullText .. &#039;#&#039; .. id&lt;br /&gt;
							table.insert(values, id) &lt;br /&gt;
						end&lt;br /&gt;
						properties = p.processStatement({subject=properties, statement=subproperties_res.properties, debug=debug}).subject&lt;br /&gt;
					else --list array&lt;br /&gt;
						for i, e in pairs(v) do&lt;br /&gt;
							if (type(e) == &#039;table&#039;) then &lt;br /&gt;
								local subproperties_res = p.getSemanticProperties({jsonschema=schema, jsondata=e, properties=p.copy(subobject_properties), store=true, root=false, debug=debug, context=context, subschema=schema_properties[k], parent_schema_property=property_data[k]})&lt;br /&gt;
								local id = subproperties_res.id --subobject_id&lt;br /&gt;
								if (id ~= nil) then &lt;br /&gt;
									id = mw.title.getCurrentTitle().fullText .. &#039;#&#039; .. id&lt;br /&gt;
									table.insert(values, id) &lt;br /&gt;
								end&lt;br /&gt;
								properties = p.processStatement({subject=properties, statement=subproperties_res.properties, debug=debug}).subject&lt;br /&gt;
							else values = v end --plain strings&lt;br /&gt;
						end&lt;br /&gt;
					end &lt;br /&gt;
					for pi, property_name in ipairs(property_names) do&lt;br /&gt;
						for i,value in pairs(values) do table.insert(properties[property_name], value) end&lt;br /&gt;
						if (debug) then mw.logObject(&amp;quot;set &amp;quot; .. property_name .. &amp;quot; = &amp;quot; .. mw.dumpObject(values)) end&lt;br /&gt;
					end&lt;br /&gt;
				else if (debug) then mw.logObject(&amp;quot;not mapped: &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end &lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				if (mapping_found) then &lt;br /&gt;
					for pi, property_name in ipairs(property_names) do&lt;br /&gt;
						table.insert(properties[property_name], v)&lt;br /&gt;
						if (debug) then mw.logObject(&amp;quot;set &amp;quot; .. property_name .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
					end&lt;br /&gt;
				else &lt;br /&gt;
					if (debug) then mw.logObject(&amp;quot;not mapped: &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end &lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local store_res = nil&lt;br /&gt;
	if (store) then &lt;br /&gt;
		properties[&#039;HasOswId&#039;] = subjectId&lt;br /&gt;
		if (root) then &lt;br /&gt;
			if (debug) then mw.logObject(&amp;quot;Store page properties&amp;quot;) end&lt;br /&gt;
			store_res = mw.smw.set( properties ) --store as semantic properties&lt;br /&gt;
		else&lt;br /&gt;
			properties[&#039;@category&#039;] = {}&lt;br /&gt;
			p.tableMerge(properties[&#039;@category&#039;], jsondata[p.keys.category]) -- from json property &#039;type&#039;&lt;br /&gt;
			p.tableMerge(properties[&#039;@category&#039;], properties[p.keys.category_pseudoproperty]) -- from json-ld context &#039;Property:Category&#039;&lt;br /&gt;
			properties[p.keys.category_pseudoproperty] = nil -- delete pseudo property&lt;br /&gt;
			if (jsondata[p.keys.name] ~= nil) then properties[&#039;Display title of&#039;] = jsondata[p.keys.name] &lt;br /&gt;
			elseif (jsondata[p.keys.label] ~= nil and jsondata[p.keys.label][1] ~= nil) then properties[&#039;Display title of&#039;] = p.splitString(jsondata[p.keys.label][1], &#039;@&#039;)[1] &lt;br /&gt;
			else properties[&#039;Display title of&#039;] = p.defaultArg(subschema[&#039;title&#039;], &amp;quot;&amp;quot;) end&lt;br /&gt;
			p.setNormalizedLabel(properties) --build normalized multilang label&lt;br /&gt;
			if (p.tableLength(properties) &amp;gt; 0) then&lt;br /&gt;
				store_res = mw.smw.subobject( properties, subobjectId )	--store as subobject&lt;br /&gt;
				if (debug) then mw.logObject(&amp;quot;Store subobject with id &amp;quot; .. (subobjectId or &amp;quot;&amp;lt;random&amp;gt;&amp;quot;)) end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then mw.logObject(properties) end&lt;br /&gt;
	if (store_res ~= nil) then &lt;br /&gt;
		if (not store_res and store_res.error ~= nil) then error = error .. store_res.error end&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then mw.logObject(error) end&lt;br /&gt;
	return {properties=properties, definitions=property_data, id=subobjectId, context=context, error=error}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.processStatement(args)&lt;br /&gt;
	local statement = p.defaultArg(args.statement)&lt;br /&gt;
	local subject = p.defaultArg(args.subject)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
&lt;br /&gt;
	-- handle &amp;quot;approved&amp;quot; statements&lt;br /&gt;
	if (statement[&amp;quot;HasSubject&amp;quot;] == nil or statement[&amp;quot;HasSubject&amp;quot;][1] == nil or statement[&amp;quot;HasSubject&amp;quot;][1] == &amp;quot;&amp;quot;) then --implicit subject&lt;br /&gt;
		if (statement[&amp;quot;HasProperty&amp;quot;] ~= nil and statement[&amp;quot;HasProperty&amp;quot;][1] ~= nil and statement[&amp;quot;HasProperty&amp;quot;][1] ~= &amp;quot;&amp;quot; and statement[&amp;quot;HasObject&amp;quot;] ~= nil) then&lt;br /&gt;
			local property = string.gsub(statement[&amp;quot;HasProperty&amp;quot;][1], p.keys.property_ns_prefix .. &amp;quot;:&amp;quot;, &amp;quot;&amp;quot;) -- also allow prefix properties like: Property:schema:url&lt;br /&gt;
			if (debug) then&lt;br /&gt;
				mw.log(&amp;quot;Set property &amp;quot; .. property .. &amp;quot; from statement to &amp;quot;)&lt;br /&gt;
				mw.logObject(statement[&amp;quot;HasObject&amp;quot;])&lt;br /&gt;
			end&lt;br /&gt;
			if (subject[property] == nil) then subject[property] = {} end&lt;br /&gt;
			for k, v in pairs(statement[&amp;quot;HasObject&amp;quot;]) do table.insert(subject[property], v) end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return {subject=subject}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- build a semantic query based on provided properties and their schema definition&lt;br /&gt;
--[[ test: &lt;br /&gt;
mw.logObject(p.getSemanticQuery({&lt;br /&gt;
	jsonschema={&lt;br /&gt;
		[&amp;quot;@context&amp;quot;]={&lt;br /&gt;
			test=&amp;quot;Property:TestProperty&amp;quot;,&lt;br /&gt;
			number_max=&amp;quot;Property:HasNumber&amp;quot;,&lt;br /&gt;
			date_min=&amp;quot;Property:HasDate&amp;quot;&lt;br /&gt;
		}, &lt;br /&gt;
		properties={&lt;br /&gt;
			test={title=&amp;quot;Test&amp;quot;, type=&amp;quot;string&amp;quot;},&lt;br /&gt;
			number_max={title=&amp;quot;Number&amp;quot;, type=&amp;quot;string&amp;quot;, format=&amp;quot;number&amp;quot;, options={role={query={filter=&amp;quot;max&amp;quot;}}}},&lt;br /&gt;
			date_min={title=&amp;quot;Date&amp;quot;, type=&amp;quot;string&amp;quot;, format=&amp;quot;date&amp;quot;, options={role={query={filter=&amp;quot;min&amp;quot;}}}},&lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	jsondata={test=&amp;quot;TestValue&amp;quot;, number_max=5, date_min=&amp;quot;01.01.2023&amp;quot;}&lt;br /&gt;
}))&lt;br /&gt;
--]]&lt;br /&gt;
function p.getSemanticQuery(args)&lt;br /&gt;
	--local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	--local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	local where = &amp;quot;&amp;quot;&lt;br /&gt;
	local select = &amp;quot;&amp;quot;&lt;br /&gt;
	local semantic_properties = p.getSemanticProperties(args)&lt;br /&gt;
	--mw.logObject(semantic_properties)&lt;br /&gt;
	for k,def in pairs(semantic_properties.definitions) do&lt;br /&gt;
		-- see also: https://www.semantic-mediawiki.org/wiki/Help:Search_operators&lt;br /&gt;
		local filter = p.defaultArgPath(def.schema_data, {&#039;options&#039;, &#039;role&#039;, &#039;query&#039;, &#039;filter&#039;}, &#039;eq&#039;)&lt;br /&gt;
		local value = def.value&lt;br /&gt;
		if def.schema_data.type == &#039;string&#039; and (def.schema_data.format == &#039;number&#039; or def.schema_data.format == &#039;date&#039;) then &lt;br /&gt;
			if (filter == &#039;min&#039;) then value = &amp;quot;&amp;lt;&amp;quot; .. value&lt;br /&gt;
			elseif (filter == &#039;max&#039;) then value = &amp;quot;&amp;gt;&amp;quot; .. value&lt;br /&gt;
			else value = value end --exact match&lt;br /&gt;
		elseif def.schema_data.type == &#039;string&#039; then&lt;br /&gt;
			value = &amp;quot;~*&amp;quot; .. value .. &amp;quot;*&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		where = where .. &amp;quot;\n[[&amp;quot;.. def.property .. &amp;quot;::&amp;quot; .. value .. &amp;quot;]]&amp;quot;&lt;br /&gt;
		select = select .. &amp;quot;\n|?&amp;quot; .. def.property&lt;br /&gt;
		if (def.schema_data.title ~= nil) then select = select .. &amp;quot;=&amp;quot; .. def.schema_data.title end&lt;br /&gt;
	end&lt;br /&gt;
	if (where ~= &amp;quot;&amp;quot;) then res = &amp;quot;{{#ask:&amp;quot; .. res .. where .. select .. &amp;quot;}}&amp;quot; end&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- HELPERS&lt;br /&gt;
&lt;br /&gt;
-- expands all $ref&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={items={test=&amp;quot;value&amp;quot;, [&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/JsonSchema:Label?action=raw&amp;quot;}}}).json)&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Item?action=raw&amp;amp;slot=jsonschema&amp;quot;}}).json)&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/JsonSchema:Statement?action=raw&amp;quot;}}).json)&lt;br /&gt;
function p.expandJsonRef(args)&lt;br /&gt;
	local json = p.defaultArg(args.json, {})&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local refs = {}&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if (k == &amp;quot;$ref&amp;quot;) then&lt;br /&gt;
    		-- e. g. &amp;quot;/wiki/JsonSchema:Label?action=raw&amp;quot; or &amp;quot;/wiki/Category:Entity?action=raw&amp;amp;slot=jsonschema&amp;quot;&lt;br /&gt;
    		if string.find(v, &amp;quot;#&amp;quot;) then&lt;br /&gt;
    			if (debug) then mw.logObject(&amp;quot;Skip relative reference&amp;quot;) end&lt;br /&gt;
    		else&lt;br /&gt;
	    		local uri = mw.uri.new(v)&lt;br /&gt;
	    		local ref_title = mw.text.split(uri.path, &amp;quot;wiki/&amp;quot;, true)[2]&lt;br /&gt;
	    		local ref_slot = uri.query[&amp;quot;slot&amp;quot;]&lt;br /&gt;
	    		if (debug) then &lt;br /&gt;
		    		if (ref_slot ~= nil) then mw.logObject(&amp;quot;Ref found with title &amp;quot; .. ref_title .. &amp;quot; and slot &amp;quot; .. ref_slot)&lt;br /&gt;
		    		else mw.logObject(&amp;quot;Ref found with title &amp;quot; .. ref_title) end&lt;br /&gt;
	    		end&lt;br /&gt;
	    		local ref_json = p.loadJson({title=ref_title, slot=ref_slot}).json&lt;br /&gt;
	    		refs[v] = ref_json&lt;br /&gt;
	    		json[k] = nil&lt;br /&gt;
    		end&lt;br /&gt;
    	end&lt;br /&gt;
    end&lt;br /&gt;
	--mw.logObject(refs)&lt;br /&gt;
	for k,v in pairs(refs) do&lt;br /&gt;
		json = p.tableMerge(v, json)&lt;br /&gt;
	end&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
            json[k] = p.expandJsonRef({json=v}).json&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    local result = p.copy(json)&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if (k == &amp;quot;allOf&amp;quot;) then&lt;br /&gt;
            if (type(v) == &amp;quot;table&amp;quot; and v[1] == nil) then v = {v} end -- ensure array&lt;br /&gt;
            for i,s in pairs(v) do &lt;br /&gt;
            	result = p.tableMerge(s, result)&lt;br /&gt;
            	if (debug) then mw.log(&amp;quot;merge allOf with title &amp;quot; .. s[&amp;quot;title&amp;quot;]) end&lt;br /&gt;
            end&lt;br /&gt;
            result[k] = nil&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return {json=result}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function p.defaultArg(arg, default)&lt;br /&gt;
	if (arg == nil) then &lt;br /&gt;
		return default&lt;br /&gt;
	else&lt;br /&gt;
		return arg&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- returns the value of a table (dict) path or default, if the path is not defined&lt;br /&gt;
-- test: mw.logObject(p.defaultArgPath({some={defined={path=&amp;quot;value&amp;quot;}}}, {&amp;quot;some&amp;quot;, &amp;quot;defined&amp;quot;, &amp;quot;path&amp;quot;}, &amp;quot;default_value&amp;quot;))&lt;br /&gt;
-- test: mw.logObject(p.defaultArgPath({some={defined={path=&amp;quot;value&amp;quot;}}}, {&amp;quot;some&amp;quot;, &amp;quot;undefined&amp;quot;, &amp;quot;path&amp;quot;}, &amp;quot;default_value&amp;quot;))&lt;br /&gt;
function p.defaultArgPath(arg, path, default)&lt;br /&gt;
	if (arg == nil) then &lt;br /&gt;
		return default&lt;br /&gt;
	elseif (path == nil) then&lt;br /&gt;
		return arg&lt;br /&gt;
	else&lt;br /&gt;
		key = table.remove(path,1)&lt;br /&gt;
		if (key == nil) then return arg end  --end of path&lt;br /&gt;
		return p.defaultArgPath(arg[key], path, default)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.splitString(inputstr, sep)&lt;br /&gt;
	&lt;br /&gt;
        if sep == nil then&lt;br /&gt;
                sep = &amp;quot;;&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        local t={}&lt;br /&gt;
        for str in string.gmatch(inputstr, &amp;quot;([^&amp;quot;..sep..&amp;quot;]+)&amp;quot;) do&lt;br /&gt;
                table.insert(t, str)&lt;br /&gt;
        end&lt;br /&gt;
        return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--dumps a table to a string (replaced by mw.dumpObject())&lt;br /&gt;
function p.dump(o)&lt;br /&gt;
   return mw.dumpObject(o)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--converts a literal to an table&lt;br /&gt;
function p.tablefy(o)&lt;br /&gt;
	if (o == nil) then o = {} end&lt;br /&gt;
	if (type(o) ~= &#039;table&#039;) then o = {o} end&lt;br /&gt;
	return o&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--true if the value is contained in the array (flat arrays only)&lt;br /&gt;
function p.tableContains (tab, val)&lt;br /&gt;
    for index, value in ipairs(tab) do&lt;br /&gt;
        if value == val then&lt;br /&gt;
            return true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--get the size of a table&lt;br /&gt;
function p.tableLength(t)&lt;br /&gt;
  local count = 0&lt;br /&gt;
  for _ in pairs(t) do count = count + 1 end&lt;br /&gt;
  return count&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--check if a variable is nil or an empty string or table&lt;br /&gt;
function p.nilOrEmpty(o)&lt;br /&gt;
	if (o == nil) then return true&lt;br /&gt;
	elseif (type(o) == &#039;string&#039; and o == &amp;quot;&amp;quot;) then return true&lt;br /&gt;
	elseif (type(o) == &#039;table&#039; and p.tableLength(o) == 0) then return true&lt;br /&gt;
	else return false &lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- merges t2 to t1&lt;br /&gt;
--test: mw.logObject(p.tableMerge({&amp;quot;string&amp;quot;, test1=&amp;quot;test1&amp;quot;, subtable1={&amp;quot;test&amp;quot;}}, {&amp;quot;string2&amp;quot;, test1=&amp;quot;test2&amp;quot;, test3=&amp;quot;test4&amp;quot;}))&lt;br /&gt;
function p.tableMerge(t1, t2)&lt;br /&gt;
	if (t1 == nil) then t1 = {} elseif (type(t1) ~= &#039;table&#039;) then t1 = {t1} end&lt;br /&gt;
	if (t2 == nil) then t2 = {} elseif (type(t2) ~= &#039;table&#039;) then t2 = {t2} end&lt;br /&gt;
    for k,v in pairs(t2) do&lt;br /&gt;
        if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
            if type(t1[k] or false) == &amp;quot;table&amp;quot; then&lt;br /&gt;
                p.tableMerge(t1[k] or {}, t2[k] or {})&lt;br /&gt;
            else&lt;br /&gt;
                if type(k) == &#039;number&#039; then table.insert(t1, v)&lt;br /&gt;
            	else t1[k] = v end&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
        	if type(k) == &#039;number&#039; then table.insert(t1, v)&lt;br /&gt;
            else t1[k] = v end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return t1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- from https://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value&lt;br /&gt;
function p.copy(obj, seen)&lt;br /&gt;
  if type(obj) ~= &#039;table&#039; then return obj end&lt;br /&gt;
  if seen and seen[obj] then return seen[obj] end&lt;br /&gt;
  local s = seen or {}&lt;br /&gt;
  local res = setmetatable({}, getmetatable(obj))&lt;br /&gt;
  s[obj] = res&lt;br /&gt;
  for k, v in pairs(obj) do res[p.copy(k, s)] = p.copy(v, s) end&lt;br /&gt;
  return res&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- build normalized multilang label&lt;br /&gt;
function p.setNormalizedLabel(properties, use_fallbacks)&lt;br /&gt;
	if (use_fallbacks == nil) then use_fallbacks = true end&lt;br /&gt;
	if (properties[&#039;HasLabel&#039;] ~= nil) then &lt;br /&gt;
		labels = properties[&#039;HasLabel&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = p.splitString(label, &#039;@&#039;)[1]:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			label_lang = &amp;quot;en&amp;quot;&lt;br /&gt;
			if (p.splitString(label, &#039;@&#039;)[2] ~= nil) then label_lang = p.splitString(label, &#039;@&#039;)[2] end&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@&amp;quot; .. label_lang)	&lt;br /&gt;
		end&lt;br /&gt;
	&lt;br /&gt;
	elseif (use_fallbacks and properties[&#039;HasName&#039;] ~= nil) then -- fallback, assume English lang&lt;br /&gt;
		labels = properties[&#039;HasName&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@en&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	elseif (use_fallbacks and properties[&#039;Display title of&#039;] ~= nil) then -- fallback, assume English lang&lt;br /&gt;
		labels = properties[&#039;Display title of&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@en&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:Entity&amp;diff=4068</id>
		<title>Category:Entity</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:Entity&amp;diff=4068"/>
		<updated>2025-02-21T07:16:37Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Reverted edits by 0000-0003-0410-3616 (talk) to last revision by 0000-0001-7444-2969&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:Item&amp;diff=4067</id>
		<title>Category:Item</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:Item&amp;diff=4067"/>
		<updated>2025-02-21T06:57:51Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW57beed5e1294434ba77bb6516e461456&amp;diff=4066</id>
		<title>Category:OSW57beed5e1294434ba77bb6516e461456</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW57beed5e1294434ba77bb6516e461456&amp;diff=4066"/>
		<updated>2025-02-21T06:57:40Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Install package: OSW Ontology&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=JsonSchema:PostalAddress&amp;diff=4065</id>
		<title>JsonSchema:PostalAddress</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=JsonSchema:PostalAddress&amp;diff=4065"/>
		<updated>2025-02-21T06:57:34Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
    &amp;quot;@context&amp;quot;: {&lt;br /&gt;
        &amp;quot;address_supplement&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasAddressSupplement&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;xsd:string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;city&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasCity&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;city*&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;schema:containedInPlace&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;city**&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:IsLocatedIn&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;country&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;schema:addressCountry&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;country*&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasCountry&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;house_no&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasHouseNo&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;xsd:string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;postal_code&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;schema:postalCode&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;xsd:string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;postal_code*&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasPostalCode&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;xsd:string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;post_office_box_no&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;schema:postOfficeBoxNumer&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;xsd:string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;post_office_box_no*&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasPoBoxNo&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;xsd:string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;region&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;schema:addressRegion&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;region*&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasRegion&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;state&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;schema:addressCountry&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;state*&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasState&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;@id&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;street_name&amp;quot;: {&lt;br /&gt;
            &amp;quot;@id&amp;quot;: &amp;quot;Property:HasStreet&amp;quot;,&lt;br /&gt;
            &amp;quot;@type&amp;quot;: &amp;quot;xsd:string&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;object&amp;quot;,&lt;br /&gt;
    &amp;quot;title&amp;quot;: &amp;quot;Postal address&amp;quot;,&lt;br /&gt;
    &amp;quot;title*&amp;quot;: {&lt;br /&gt;
        &amp;quot;en&amp;quot;: &amp;quot;Postal address&amp;quot;,&lt;br /&gt;
        &amp;quot;de&amp;quot;: &amp;quot;Postalische Adresse&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
    &amp;quot;eval_template&amp;quot;: {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;wikitext&amp;quot;,&lt;br /&gt;
        &amp;quot;mode&amp;quot;: &amp;quot;render&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: &amp;quot;{{#if: {{{addressee|}}}|{{{addressee|}}}\u003Cbr\u003E|}}{{{street_name|}}} {{{house_no|}}}\u003Cbr\u003E{{#if: {{{address_supplement|}}}|{{{address_supplement|}}}\u003Cbr\u003E|}}{{{postal_code|}}} {{#if: {{{city|}}} |[[{{{city|}}}]]|{{{city_name|}}} }}, {{#if: {{{region|}}}|[[{{{region|}}}]],\u0026nbsp;|}}{{#if: {{{state|}}}|[[{{{state|}}}]],\u0026nbsp;|}}{{#if: {{{country|}}} |[[{{{country|}}}]]|{{{country_name|}}} }}{{#if: {{{post_office_box_no|}}}|\u003Cbr\u003E{{#switch:{{USERLANGUAGECODE}}|#default=PO Box No|de=Postfach-Nr.}} {{{post_office_box_no|}}}|}}&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;required&amp;quot;: [&lt;br /&gt;
        &amp;quot;uuid&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;defaultProperties&amp;quot;: [&lt;br /&gt;
        &amp;quot;uuid&amp;quot;,&lt;br /&gt;
        &amp;quot;street_name&amp;quot;,&lt;br /&gt;
        &amp;quot;house_no&amp;quot;,&lt;br /&gt;
        &amp;quot;postal_code&amp;quot;,&lt;br /&gt;
        &amp;quot;city&amp;quot;,&lt;br /&gt;
        &amp;quot;city_name&amp;quot;,&lt;br /&gt;
        &amp;quot;country&amp;quot;,&lt;br /&gt;
        &amp;quot;country_name&amp;quot;,&lt;br /&gt;
        &amp;quot;address_supplement&amp;quot;,&lt;br /&gt;
        &amp;quot;state&amp;quot;,&lt;br /&gt;
        &amp;quot;region&amp;quot;,&lt;br /&gt;
        &amp;quot;post_office_box_no&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;properties&amp;quot;: {&lt;br /&gt;
        &amp;quot;uuid&amp;quot;: {&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;format&amp;quot;: &amp;quot;uuid&amp;quot;,&lt;br /&gt;
            &amp;quot;options&amp;quot;: {&lt;br /&gt;
                &amp;quot;hidden&amp;quot;: true&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;country&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;Country&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;Country&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Land&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;format&amp;quot;: &amp;quot;autocomplete&amp;quot;,&lt;br /&gt;
            &amp;quot;range&amp;quot;: &amp;quot;Category:OSW0551abcd6f734047825e3ded4c8a0ffe&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;country_name&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;Country (text)&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Land (Text)&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;state&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;State&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;State&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Bundesland&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;format&amp;quot;: &amp;quot;autocomplete&amp;quot;,&lt;br /&gt;
            &amp;quot;range&amp;quot;: &amp;quot;Category:OSWab60f9a227954ee0be92344ff6272420&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;region&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;Region&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;Region&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Region&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;format&amp;quot;: &amp;quot;autocomplete&amp;quot;,&lt;br /&gt;
            &amp;quot;range&amp;quot;: &amp;quot;Category:OSW95efaf34e2c7439e8e7967233910e44b&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;post_office_box_no&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;PO box number&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;PO box number&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Postfach nummer&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;postal_code&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;Postal code&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;Postal code&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Postleitzahl&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;city&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;City&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;City&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Stadt&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;format&amp;quot;: &amp;quot;autocomplete&amp;quot;,&lt;br /&gt;
            &amp;quot;range&amp;quot;: &amp;quot;Category:OSW807f1da5b42e42f296b213ab06ca873b&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;city_name&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;City (text)&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Stadt (Text)&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;street_name&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;Street&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;Street&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Straße&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;house_no&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;House number&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;House number&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Hausnummer&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;address_supplement&amp;quot;: {&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;Address supplement&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;en&amp;quot;: &amp;quot;Address supplement&amp;quot;,&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Adresszusatz&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;addressee&amp;quot;: {&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
            &amp;quot;title&amp;quot;: &amp;quot;Addressee&amp;quot;,&lt;br /&gt;
            &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Adressat&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;description&amp;quot;: &amp;quot;Title line of the address in case different from organization name&amp;quot;,&lt;br /&gt;
            &amp;quot;description*&amp;quot;: {&lt;br /&gt;
                &amp;quot;de&amp;quot;: &amp;quot;Titelzeile der Adresse falls abweichend vom Organisationsname&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Module:Viewer/Link&amp;diff=4064</id>
		<title>Module:Viewer/Link</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Module:Viewer/Link&amp;diff=4064"/>
		<updated>2025-02-21T06:57:28Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
local p = {} --p stands for package&lt;br /&gt;
&lt;br /&gt;
function p.render(frame)&lt;br /&gt;
	if (frame.args[&#039;page&#039;] == nil and frame.args[&#039;url&#039;] == nil) then frame = frame:getParent() end&lt;br /&gt;
	local page = frame.args[&#039;page&#039;]&lt;br /&gt;
	local url = frame.args[&#039;url&#039;]&lt;br /&gt;
	local label = frame.args[&#039;label&#039;]&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	if page ~= nil and page ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		page = string.gsub(page, &amp;quot;Category:&amp;quot;, &amp;quot;:Category:&amp;quot;)&lt;br /&gt;
		if label == nil or label == &amp;quot;&amp;quot; then&lt;br /&gt;
			label = nil&lt;br /&gt;
			local pref_lang = frame.args[&#039;pref_lang&#039;]&lt;br /&gt;
			if pref_lang == nil then pref_lang = frame:preprocess( &amp;quot;{{USERLANGUAGECODE}}&amp;quot; ) end&lt;br /&gt;
			local query = &amp;quot;[[&amp;quot; .. page .. &amp;quot;]]|?HasLabel=label_pref_lang|+lang=&amp;quot; .. pref_lang .. &amp;quot;|?HasLabel=label_lang_en|+lang=en|?HasLabel#-=label_lang_any|?HasName=name|?Display_title_of=displaytitle|mainlabel=-&amp;quot;&lt;br /&gt;
			local result = mw.smw.ask( query )&lt;br /&gt;
			mw.logObject(result)&lt;br /&gt;
			if result ~= nil and result[1] ~= nil then&lt;br /&gt;
				label = result[1][&#039;label_pref_lang&#039;]&lt;br /&gt;
				if label == nil then label = result[1][&#039;label_lang_en&#039;] end&lt;br /&gt;
				if label == nil then label = result[1][&#039;label_lang_any&#039;] end&lt;br /&gt;
				if label == nil then label = result[1][&#039;displaytitle&#039;] end&lt;br /&gt;
				if label == nil then label = result[1][&#039;name&#039;] end&lt;br /&gt;
				if type(label) == &#039;table&#039; then label = label[1] end&lt;br /&gt;
				--mw.logObject(result)&lt;br /&gt;
				--result = result[1][&#039;predecessor&#039;]&lt;br /&gt;
				--mw.logObject(result)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		page = string.gsub(page, &amp;quot;File:&amp;quot;, &amp;quot;Media:&amp;quot;)&lt;br /&gt;
		wikitext = &amp;quot;[[&amp;quot; .. page&lt;br /&gt;
		if label ~= nil then wikitext = wikitext .. &amp;quot;|&amp;quot; .. label end &lt;br /&gt;
		wikitext = wikitext .. &amp;quot;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if url ~= nil and url ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		if label == nil then label = url end&lt;br /&gt;
		wikitext = &amp;quot;[&amp;quot; .. url .. &amp;quot; &amp;quot; .. label .. &amp;quot;]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wikitext = frame:preprocess( wikitext )&lt;br /&gt;
	&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.debug()&lt;br /&gt;
	frame = mw.getCurrentFrame() -- Get a frame object&lt;br /&gt;
	newFrame = frame:newChild{ title=title, args = {page=&amp;quot;Category:Entity&amp;quot;, xpref_lang=&amp;quot;de&amp;quot;}}&lt;br /&gt;
	mw.logObject(p.render( newFrame ) ) &lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Template:Viewer/Link&amp;diff=4063</id>
		<title>Template:Viewer/Link</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Template:Viewer/Link&amp;diff=4063"/>
		<updated>2025-02-21T06:57:27Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;onlyinclude&amp;gt;{{#invoke:Viewer/Link|render}}&amp;lt;/onlyinclude&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Test ==&lt;br /&gt;
* {{{{FULLPAGENAME}}|page=Main Page}} test&lt;br /&gt;
* {{{{FULLPAGENAME}}|page=Main Page|label=The main page}} test&lt;br /&gt;
* {{{{FULLPAGENAME}}|page=Main Page|url=|label=}} test&lt;br /&gt;
* {{{{FULLPAGENAME}}|url=https://google.com}} test&lt;br /&gt;
* {{{{FULLPAGENAME}}|page=Category:Entity|url=|label=}} category test&lt;br /&gt;
* {{{{FULLPAGENAME}}|page=File:OSW5f36a59d4bb94ea0bf93f08f7470f609.png}} file test&lt;br /&gt;
* {{{{FULLPAGENAME}}|page=Item:OSW9ac4532e584340bcadab9022402a9697#OSW2ac7f0e6172e4fb88d61527e493c3366}} subobject test&lt;br /&gt;
&lt;br /&gt;
&amp;lt;templatedata&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;params&amp;quot;: {&lt;br /&gt;
		&amp;quot;page&amp;quot;: {&lt;br /&gt;
			&amp;quot;label&amp;quot;: &amp;quot;Link target&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;label&amp;quot;: {}&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;format&amp;quot;: &amp;quot;inline&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/templatedata&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Module:MwJson&amp;diff=4062</id>
		<title>Module:MwJson</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Module:MwJson&amp;diff=4062"/>
		<updated>2025-02-21T06:57:27Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
&lt;br /&gt;
local lustache = require(&amp;quot;Module:Lustache&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local p = {} --p stands for package&lt;br /&gt;
&lt;br /&gt;
p.keys = { --jsonschema / json-ld keys&lt;br /&gt;
	category=&#039;type&#039;, &lt;br /&gt;
	category_pseudoproperty=&#039;Category&#039;, -- Property:Category&lt;br /&gt;
	subcategory=&#039;subclass_of&#039;,&lt;br /&gt;
	schema_type=&#039;schema_type&#039;,&lt;br /&gt;
	property_ns_prefix=&#039;Property&#039;,&lt;br /&gt;
	schema=&#039;osl_schema&#039;, &lt;br /&gt;
	template=&#039;eval_template&#039;,&lt;br /&gt;
	mode=&#039;mode&#039;,&lt;br /&gt;
	context=&#039;@context&#039;,&lt;br /&gt;
	allOf=&#039;allOf&#039;,&lt;br /&gt;
	label=&#039;label&#039;,&lt;br /&gt;
	name=&#039;name&#039;,&lt;br /&gt;
	description=&#039;description&#039;,&lt;br /&gt;
	text=&#039;text&#039;,&lt;br /&gt;
	debug=&#039;_debug&#039;&lt;br /&gt;
} &lt;br /&gt;
p.slots = { --slot names&lt;br /&gt;
	main=&#039;main&#039;,&lt;br /&gt;
	jsondata=&#039;jsondata&#039;, &lt;br /&gt;
	jsonschema=&#039;jsonschema&#039;, &lt;br /&gt;
	header_template=&#039;header_template&#039;,&lt;br /&gt;
	footer_template=&#039;footer_template&#039;,&lt;br /&gt;
	data_template=&#039;data_template&#039;&lt;br /&gt;
} &lt;br /&gt;
p.mode = {&lt;br /&gt;
	header=&#039;header&#039;,&lt;br /&gt;
	footer=&#039;footer&#039;,&lt;br /&gt;
	query=&#039;query&#039;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
p.cache = {}&lt;br /&gt;
&lt;br /&gt;
--loads json from a wiki page&lt;br /&gt;
--test: mw.logObject(p.loadJson({title=&amp;quot;JsonSchema:Entity&amp;quot;}))&lt;br /&gt;
--test: mw.logObject(p.loadJson({title=&amp;quot;Category:Entity&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}))&lt;br /&gt;
function p.loadJson(args)&lt;br /&gt;
	local page_title = p.defaultArg(args.title, &amp;quot;JsonSchema:Entity&amp;quot;) --for testing&lt;br /&gt;
	local slot = p.defaultArg(args.slot, &#039;main&#039;)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, nil)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local json = {}&lt;br /&gt;
	&lt;br /&gt;
	if p.cache[page_title] ~= nil then&lt;br /&gt;
		if p.cache[page_title][slot] ~= nil then&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. p.slots.jsondata .. &amp;quot; of page &amp;quot; .. page_title .. &amp;quot; from cache &amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			json = p.cache[page_title][slot]&lt;br /&gt;
			return {json=json, debug_msg=msg}&lt;br /&gt;
		end&lt;br /&gt;
	else p.cache[page_title] = {}&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if (slot == &#039;main&#039;) then&lt;br /&gt;
		--json = mw.loadJsonData( &amp;quot;JsonSchema:Entity&amp;quot; ) --requires MediaWiki 1.39&lt;br /&gt;
		local page = mw.title.makeTitle(p.splitString(page_title, &#039;:&#039;)[1], p.splitString(page_title, &#039;:&#039;)[2])&lt;br /&gt;
		local text = page:getContent()&lt;br /&gt;
		if (text ~= nil) then json = mw.text.jsonDecode(text) end&lt;br /&gt;
	else&lt;br /&gt;
		if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. p.slots.jsondata .. &amp;quot; of page &amp;quot; .. page_title .. &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		local text = mw.slots.slotContent( slot , page_title )&lt;br /&gt;
		if (text ~= nil) then json = mw.text.jsonDecode(text) end&lt;br /&gt;
	end	&lt;br /&gt;
	&lt;br /&gt;
	--mw.logObject(json)&lt;br /&gt;
	p.cache[page_title][slot] = json&lt;br /&gt;
&lt;br /&gt;
	return {json=json, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- test: mw.logObject(p.walkJsonSchema({jsonschema=p.loadJson({title=&amp;quot;Category:Hardware&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema)&lt;br /&gt;
function p.walkJsonSchema(args)&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local jsonschemas = p.defaultArg(args.jsonschemas, {})&lt;br /&gt;
	local categories = p.defaultArg(args.categories, nil)&lt;br /&gt;
	local visited = p.defaultArg(args.visited, {})&lt;br /&gt;
	local mode = p.defaultArg(args.mode, p.mode.header)&lt;br /&gt;
	--local merged_jsonschema = p.defaultArg(args.merged_jsonschema, {})&lt;br /&gt;
	local templates = p.defaultArg(args.templates, {})&lt;br /&gt;
	local recursive = p.defaultArg(args.recursive, true)&lt;br /&gt;
	local root = p.defaultArg(args.root, true)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local category_template_slot = nil&lt;br /&gt;
	if (mode == p.mode.footer) then category_template_slot = p.slots.footer_template end&lt;br /&gt;
	if (mode == p.mode.header) then category_template_slot = p.slots.header_template end&lt;br /&gt;
	&lt;br /&gt;
	if (categories == nil) then categories = p.getCategories({jsonschema=jsonschema, includeNamespace=true}).categories end&lt;br /&gt;
	if (type(categories) ~= &#039;table&#039;) then categories = {categories} end&lt;br /&gt;
	if (debug) then msg = msg .. &amp;quot;Supercategories: &amp;quot; .. mw.dumpObject(categories) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	for k, category in pairs(categories) do&lt;br /&gt;
		if (not p.tableContains(visited, category)) then&lt;br /&gt;
			--mw.logObject(&amp;quot;Visit &amp;quot; .. category)&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. p.slots.jsonschema .. &amp;quot; from page &amp;quot; .. category .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			local super_jsonschema_str = mw.slots.slotContent( p.slots.jsonschema , category )&lt;br /&gt;
			if (super_jsonschema_str ~= nil) then&lt;br /&gt;
				super_jsonschema = mw.text.jsonDecode( super_jsonschema_str )&lt;br /&gt;
				if (recursive) then	&lt;br /&gt;
					local res = p.walkJsonSchema({jsonschema=super_jsonschema, jsonschemas=jsonschemas, templates=templates, mode=mode, visited=visited, root=false})&lt;br /&gt;
					wikitext = wikitext .. res.wikitext &lt;br /&gt;
				end&lt;br /&gt;
				--table.insert(jsonschemas, mw.text.jsonDecode( super_jsonschema_str )) --keep a copy of the schema, super_jsonschema passed by references gets modified&lt;br /&gt;
				--table.insert(jsonschemas, super_jsonschema ) &lt;br /&gt;
				--mw.logObject(&amp;quot;Store &amp;quot; .. category)&lt;br /&gt;
				table.insert(visited, category)&lt;br /&gt;
				jsonschemas[category] = mw.text.jsonDecode( super_jsonschema_str ) --keep a copy of the schema, super_jsonschema passed by references gets modified&lt;br /&gt;
				--jsonschema = p.tableMerge(jsonschema, super_jsonschema) --merge superschema is done by the caller&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Fetch slot &amp;quot; .. category_template_slot .. &amp;quot; from page &amp;quot; .. category .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			templates[category] = mw.slots.slotContent( category_template_slot , category )&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	if (root) then&lt;br /&gt;
		for i, category in ipairs(visited) do&lt;br /&gt;
			--merge all schemas. we need to make a copy here, otherwise jsonschemas[&amp;quot;Category:Entity&amp;quot;] contains the merged schema&lt;br /&gt;
			jsonschema = p.copy(p.tableMerge(jsonschema, jsonschemas[category])) &lt;br /&gt;
		end	&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then wikitext = msg .. wikitext  end&lt;br /&gt;
	return {jsonschema=jsonschema, jsonschemas=jsonschemas, templates=templates, visited=visited, wikitext=wikitext}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[ test: &lt;br /&gt;
category = &amp;quot;Category:Hardware&amp;quot;&lt;br /&gt;
page = &amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;&lt;br /&gt;
category2 = &amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;&lt;br /&gt;
page2 = &amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;&lt;br /&gt;
mw.logObject(p.expandEmbeddedTemplates({&lt;br /&gt;
	jsonschema=p.walkJsonSchema({jsonschema=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema, &lt;br /&gt;
	jsondata=p.loadJson({title=page, slot=&amp;quot;jsondata&amp;quot;}).json,&lt;br /&gt;
	debug=true, mode=&amp;quot;render&amp;quot;&lt;br /&gt;
}).res)&lt;br /&gt;
--]]&lt;br /&gt;
function p.expandEmbeddedTemplates(args)&lt;br /&gt;
	local frame = p.defaultArg(args.frame, mw.getCurrentFrame())&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local template = p.defaultArg(args.template, nil)&lt;br /&gt;
	local mode = p.defaultArg(args.mode, nil)&lt;br /&gt;
	local stringify_arrays = p.defaultArg(args.stringify_arrays, false)&lt;br /&gt;
	local msg = &amp;quot;&amp;quot;&lt;br /&gt;
	local res = p.defaultArg(args.jsondata, &amp;quot;&amp;quot;)&lt;br /&gt;
	&lt;br /&gt;
	for k,v in pairs(jsondata) do&lt;br /&gt;
		local eval_template = nil&lt;br /&gt;
		local eval_templates = p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, p.keys.template}, {})&lt;br /&gt;
		if (eval_templates[1] == nil) then eval_templates = {eval_templates} end --ensure list of objects&lt;br /&gt;
		for i, t in pairs(eval_templates) do&lt;br /&gt;
			if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then eval_template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
			elseif (t[p.keys.mode] == nil) then  eval_template = t --default&lt;br /&gt;
			elseif (debug) then msg = msg .. &amp;quot;Ignore eval_template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if (eval_template ~= nil and eval_template.value ~= nil and (eval_template.type == &amp;quot;mustache&amp;quot; or eval_template.type == &amp;quot;mustache-wikitext&amp;quot;)) then&lt;br /&gt;
			-- mustache can handle objects and array to we can parse it directly&lt;br /&gt;
			-- todo: handle nested templates&lt;br /&gt;
			local template_param = {[k]=v}&lt;br /&gt;
			if (eval_template.root_key == false) then template_param = v end&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Parse mustache template &amp;quot; .. eval_template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( template_param ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			jsondata[k] = lustache:render(eval_template.value, template_param, p.tableMerge({self=eval_template.value}, eval_template.partials)) -- render with self as registered partial for recursion&lt;br /&gt;
			if (eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then &lt;br /&gt;
				jsondata[k] = frame:preprocess( jsondata[k] )&lt;br /&gt;
			end&lt;br /&gt;
		elseif type(v) == &#039;table&#039; then &lt;br /&gt;
			if (v[1] == nil) then --key value array = object/dict&lt;br /&gt;
				local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=v, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays})&lt;br /&gt;
				msg = msg .. sub_res.debug_msg&lt;br /&gt;
				jsondata[k] = sub_res.res&lt;br /&gt;
				--if (sub_res.unparsed ~= nil) then jsondata[k] = sub_res.unparsed else jsondata[k] = sub_res.wikitext end&lt;br /&gt;
			else --list array&lt;br /&gt;
				local string_list = &amp;quot;&amp;quot;&lt;br /&gt;
				for i,e in pairs(v) do &lt;br /&gt;
					&lt;br /&gt;
					local eval_template = nil&lt;br /&gt;
					local eval_templates = p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;, p.keys.template}, {})&lt;br /&gt;
					if (eval_templates[1] == nil) then eval_templates = {eval_templates} end --ensure list of objects&lt;br /&gt;
					&lt;br /&gt;
					for i, t in pairs(eval_templates) do&lt;br /&gt;
						if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then eval_template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
						elseif (t[p.keys.mode] == nil) then  eval_template = t --default&lt;br /&gt;
						elseif (debug) then msg = msg .. &amp;quot;Ignore eval_template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
&lt;br /&gt;
					if type(e) == &#039;table&#039; then 	&lt;br /&gt;
						local sub_res = p.expandEmbeddedTemplates({frame=frame, jsondata=e, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays})&lt;br /&gt;
						msg = msg .. sub_res.debug_msg&lt;br /&gt;
						if (type(sub_res.res) == &#039;table&#039;) then &lt;br /&gt;
							if (debug) then msg = msg .. &amp;quot;Values for &amp;quot; .. k .. &amp;quot; contains non-literal items: &amp;quot; .. mw.dumpObject( sub_res.res ) .. &amp;quot; =&amp;gt; skip value in wikitemplate array param creation\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
						else &lt;br /&gt;
							if (stringify_arrays) then string_list = string_list .. sub_res.res .. &amp;quot;;&amp;quot; &lt;br /&gt;
							else v[i] = sub_res.res end&lt;br /&gt;
						end&lt;br /&gt;
					else&lt;br /&gt;
						if (eval_template ~= nil and eval_template.value ~= nil) then&lt;br /&gt;
							&lt;br /&gt;
							--evaluate single array item string as json {&amp;quot;self&amp;quot;: &amp;quot;&amp;lt;value&amp;gt;&amp;quot;, &amp;quot;.&amp;quot;: &amp;quot;&amp;lt;value&amp;gt;&amp;quot;} =&amp;gt; does not work since jsondata is an object&lt;br /&gt;
							--e = p.expandEmbeddedTemplates({frame=frame, jsondata={[&amp;quot;self&amp;quot;]=e,[&amp;quot;.&amp;quot;]=e}, jsonschema=p.defaultArgPath(jsonschema, {&amp;quot;properties&amp;quot;, k, &amp;quot;items&amp;quot;}, {}), template=eval_template, mode=mode, stringify_arrays=stringify_arrays})&lt;br /&gt;
							&lt;br /&gt;
&lt;br /&gt;
							if (eval_template.type == &amp;quot;mustache&amp;quot; or eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then&lt;br /&gt;
								if (debug) then msg = msg .. &amp;quot;Parse mustache template &amp;quot; .. eval_template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( e ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
								-- {{.}} in the template will be the value of e&lt;br /&gt;
								e = lustache:render(eval_template.value, e, p.tableMerge({self=eval_template.value}, eval_template.partials)) -- render with self as registered partial for recursion&lt;br /&gt;
							end&lt;br /&gt;
							if (eval_template.type == &amp;quot;mustache-wikitext&amp;quot;) then --or eval_template.type == &amp;quot;wikitext&amp;quot;) then &lt;br /&gt;
								if (debug) then msg = msg .. &amp;quot;Parse wikitext template &amp;quot; .. e .. &amp;quot; with params &amp;quot; .. mw.dumpObject( e ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
								e = frame:preprocess( e )&lt;br /&gt;
							end&lt;br /&gt;
							v[i] = e -- update array&lt;br /&gt;
						end&lt;br /&gt;
						if (stringify_arrays) then string_list = string_list .. e .. &amp;quot;;&amp;quot; end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				if (stringify_arrays) then jsondata[k] = string_list end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	if (template == nil) then &lt;br /&gt;
		local templates = jsondata[p.keys.template]&lt;br /&gt;
		if (templates == nil) then templates = p.defaultArg(jsonschema[p.keys.template], {}) end&lt;br /&gt;
		if (templates[1] == nil) then templates = {templates} end --ensure list of objects&lt;br /&gt;
		for i, t in pairs(templates) do&lt;br /&gt;
			if (t[p.keys.mode] ~= nil and t[p.keys.mode] == mode) then template = t --use only render templates in render mode and store templates in store mode&lt;br /&gt;
			elseif (t[p.keys.mode] == nil) then  template = t --default&lt;br /&gt;
			elseif (debug) then msg = msg .. &amp;quot;Ignore template&amp;quot; .. mw.dumpObject( t ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if template ~= nil then&lt;br /&gt;
		if (template.type == &amp;quot;wikitext&amp;quot;) then&lt;br /&gt;
			for k,v in pairs(jsondata) do&lt;br /&gt;
				if type(v) == &#039;table&#039; then &lt;br /&gt;
					if (debug) then msg = msg .. &amp;quot;Values for &amp;quot; .. k .. &amp;quot; contains non-literals: &amp;quot; .. mw.dumpObject( v ) .. &amp;quot; =&amp;gt; skip wikitemplate parsing\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
					return {res=res, debug_msg=msg} &lt;br /&gt;
				end --not supported&lt;br /&gt;
			end			&lt;br /&gt;
			if (template.value ~= nil) then&lt;br /&gt;
				if (debug) then msg = msg .. &amp;quot;Parse wikitemplate &amp;quot; .. template.value .. &amp;quot; with params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
				local child = frame:newChild{args=jsondata}&lt;br /&gt;
				res = child:preprocess( template.value )&lt;br /&gt;
			elseif (template.page ~= nil) then&lt;br /&gt;
				if (debug) then msg = msg .. &amp;quot;Parse wikitemplate &amp;quot; .. template.page .. &amp;quot; with params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
				res = frame:expandTemplate{ title = template.page, args = jsondata }&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--if (debug) then mw.logObject(msg) end&lt;br /&gt;
	return {res=res, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;}))&lt;br /&gt;
-- mw.logObject(p.processJsondata({jsondata=p.loadJson({title=&amp;quot;Category:OSWb3022bbf7e7146eb8e6f6e3264f50bbe&amp;quot;, slot=&amp;quot;jsondata&amp;quot;}).json, debug=true, mode=&amp;quot;header&amp;quot;, categories={&amp;quot;Category:Category&amp;quot;}}))&lt;br /&gt;
function p.processJsondata(args)&lt;br /&gt;
	local frame = p.defaultArg(args.frame, mw.getCurrentFrame())&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local template = p.defaultArg(args.template, nil)&lt;br /&gt;
	local categories = p.defaultArg(args.categories, nil)&lt;br /&gt;
	local recursive = p.defaultArg(args.recursive, true)&lt;br /&gt;
	local mode = p.defaultArg(args.mode, p.mode.header)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local title = mw.title.getCurrentTitle()&lt;br /&gt;
	&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	local msg = &amp;quot;&amp;quot; --debug msg&lt;br /&gt;
&lt;br /&gt;
	if (p.nilOrEmpty(jsondata) or (p.nilOrEmpty(categories) and p.nilOrEmpty(jsonschema) and p.nilOrEmpty(jsondata[p.keys.category]))) then return {wikitext=wikitext, debug_msg=msg} end --nothing to do here&lt;br /&gt;
	--if (jsondata == nil or p.tableLength(jsondata) == 0 or (categories == nil and jsonschema == nil and jsondata[p.keys.category] == nil)) then return {wikitext=wikitext, debug_msg=msg} end --nothing to do here&lt;br /&gt;
	--jsonschema = p.defaultArg(jsonschema, {})&lt;br /&gt;
	--jsondata = p.defaultArg(jsondata, {})&lt;br /&gt;
	--if (categories == nil) then categories = jsondata[p.keys.category] end -- let function param overwrite json property&lt;br /&gt;
	if (not p.nilOrEmpty(jsondata[p.keys.category])) then categories = jsondata[p.keys.category] end -- let json property overwrite function param&lt;br /&gt;
	&lt;br /&gt;
	local schema_res = p.walkJsonSchema({jsonschema=jsonschema, categories=categories, mode=mode, recursive=recursive, debug=debug})&lt;br /&gt;
	local expand_res = p.expandJsonRef({json=schema_res.jsonschema, debug=debug})&lt;br /&gt;
	jsonschema = expand_res.json&lt;br /&gt;
	--mw.log(mw.text.jsonEncode(jsonschema))&lt;br /&gt;
	&lt;br /&gt;
	local display_label = p.defaultArgPath(jsondata, {p.keys.name}, &amp;quot;&amp;quot;)&lt;br /&gt;
	if (display_label == &amp;quot;&amp;quot; or (title.nsText ~= &amp;quot;Category&amp;quot; and title.nsText ~= &amp;quot;Property&amp;quot;)) then &lt;br /&gt;
		display_label = p.defaultArgPath(jsondata, {p.keys.label, 1, p.keys.text}, &amp;quot;&amp;quot;) --prefere label for all non-category and non-property pages&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	local jsonld = p.copy(jsondata)&lt;br /&gt;
	local json_data_store = p.copy(jsondata)&lt;br /&gt;
	local json_data_render = p.copy(jsondata)&lt;br /&gt;
	json_res_store = p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=json_data_store, mode=&#039;store&#039;})&lt;br /&gt;
	msg = msg .. json_res_store.debug_msg&lt;br /&gt;
	--mw.log(&amp;quot;JSONDATA STORE&amp;quot;)&lt;br /&gt;
	--mw.logObject(json_res_store.res)&lt;br /&gt;
	&lt;br /&gt;
	local smw_res = nil&lt;br /&gt;
	if (mode == p.mode.header) then&lt;br /&gt;
&lt;br /&gt;
		-- get the semantic properties by looking up the json keys in the json-ld context&lt;br /&gt;
		smw_res = p.getSemanticProperties({jsonschema=jsonschema, jsondata=json_res_store.res, store=false, debug=debug})&lt;br /&gt;
		&lt;br /&gt;
		-- store metadata where properties were defined / overridden&lt;br /&gt;
		for i, category in ipairs(schema_res.visited) do &lt;br /&gt;
			for k, v in pairs(p.defaultArgPath(schema_res.jsonschemas, {category, &#039;properties&#039;}, {})) do --property section may not exisit&lt;br /&gt;
				if smw_res.definitions[k] == nil then smw_res.definitions[k] = {} end&lt;br /&gt;
				if smw_res.definitions[k][&#039;defined_in&#039;] == nil then smw_res.definitions[k][&#039;defined_in&#039;] = {} end&lt;br /&gt;
				table.insert(smw_res.definitions[k][&#039;defined_in&#039;], category)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- embed json-ld in resulting html for search engine discovery&lt;br /&gt;
		jsonld[&amp;quot;@context&amp;quot;] = smw_res.context&lt;br /&gt;
		jsonld[&amp;quot;@type&amp;quot;] = p.tableMerge(p.tablefy(jsonschema.schema_type), p.tablefy(jsonld[&amp;quot;@type&amp;quot;])) --&lt;br /&gt;
		jsonld[&#039;schema:name&#039;] = p.defaultArgPath(jsonld, {p.keys.label, 1, p.keys.text}, jsonld[&#039;name&#039;]) --google does not support @value and @lang&lt;br /&gt;
		jsonld[&#039;schema:description&#039;] = p.defaultArgPath(jsonld, {p.keys.description, 1, p.keys.text}, nil)&lt;br /&gt;
		for k, v in pairs(jsonld) do&lt;br /&gt;
			if (type(v) == &amp;quot;string&amp;quot;) then&lt;br /&gt;
				local vpart = p.splitString(v, &#039;:&#039;)&lt;br /&gt;
				if (p.tableLength(vpart) == 2 and vpart[1] == &amp;quot;File&amp;quot;) then jsonld[k] = mw.getCurrentFrame():callParserFunction( &#039;filepath&#039;, { vpart[2] } ) end --google does not follow redirects via &amp;quot;File&amp;quot;:&amp;quot;wiki:Special:Redirect/file/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		wikitext = wikitext .. &amp;quot;&amp;lt;div class=&#039;jsonld-header&#039; style=&#039;display:none&#039; data-jsonld=&#039;&amp;quot; .. mw.text.jsonEncode( jsonld ):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;`&amp;quot;) .. &amp;quot;&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local json_res = p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=json_data_render, mode=&#039;render&#039;})&lt;br /&gt;
	msg = msg .. json_res.debug_msg&lt;br /&gt;
	jsondata =json_res.res&lt;br /&gt;
	--mw.log(&amp;quot;JSONDATA RENDER&amp;quot;)&lt;br /&gt;
	--mw.logObject(jsondata)&lt;br /&gt;
&lt;br /&gt;
	local max_index = p.tableLength(schema_res.visited)&lt;br /&gt;
	for i, category in ipairs(schema_res.visited) do&lt;br /&gt;
		if (mode == p.mode.footer) then category = schema_res.visited[max_index - i +1] end --reverse order for footer templates&lt;br /&gt;
		local super_jsonschema = schema_res.jsonschemas[category]&lt;br /&gt;
		local template = schema_res.templates[category]&lt;br /&gt;
		if (template ~= nil) then&lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;Parse \n\n&amp;quot; .. template .. &amp;quot; \n\nwith params &amp;quot; .. mw.dumpObject( jsondata ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
			local stripped_jsondata={}&lt;br /&gt;
			for k, v in pairs(jsondata) do&lt;br /&gt;
				if (type(v) ~= &#039;table&#039;) then stripped_jsondata[k] = v end --delete object values, not supported by wiki templates	&lt;br /&gt;
			end&lt;br /&gt;
			local child = frame:newChild{args=stripped_jsondata}&lt;br /&gt;
			if ( template:sub(1, #&amp;quot;=&amp;quot;) == &amp;quot;=&amp;quot; ) then template = &amp;quot;\n&amp;quot; .. template end -- add line break if template starts with heading (otherwise not rendered by mw parser)&lt;br /&gt;
			wikitext = wikitext .. child:preprocess( template )&lt;br /&gt;
		elseif (mode == p.mode.header) then&lt;br /&gt;
			local ignore_properties = {[p.keys.category]=true} -- don&#039;t render type/category on every subclass&lt;br /&gt;
			for j, subcategory in ipairs(schema_res.visited) do&lt;br /&gt;
				if j &amp;gt; i then&lt;br /&gt;
					local subjsonschema = schema_res.jsonschemas[subcategory]&lt;br /&gt;
					for k, v in pairs(p.defaultArg(subjsonschema[&#039;properties&#039;], {})) do&lt;br /&gt;
						-- skip properties that are overwritten in subschemas, render them only once at the most specific position&lt;br /&gt;
						ignore_properties[k] = true&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			-- render the infobox for the schema itself and every super_schema using always the global json-ld context (merged within walkJsonSchema())&lt;br /&gt;
			-- context needs to be preprocessed with buildContext() since the generic json/table merge of the @context atttribute produces a list of strings (remote context) and context objects&lt;br /&gt;
			-- context is already build in p.getSemanticProperties. schema_allOfMerged is used to provide the full schema for overridden properties&lt;br /&gt;
			local infobox_res = p.renderInfoBox({jsonschema=super_jsonschema, schema_allOfMerged=jsonschema, context=smw_res.context, property_definitions=smw_res.definitions, jsondata=jsondata, ignore_properties=ignore_properties})&lt;br /&gt;
			wikitext = wikitext .. frame:preprocess( infobox_res.wikitext )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--local display_label = &amp;quot;&amp;quot;&lt;br /&gt;
	--if (jsondata[p.keys.label] ~= nil) then display_label = p.splitString(jsondata[p.keys.label], &#039;@&#039;)[1] end&lt;br /&gt;
	local set_categories_in_wikitext = {}&lt;br /&gt;
	p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.subcategory])  --classes/categories, nil for items&lt;br /&gt;
	if (title.nsText ~= &amp;quot;Category&amp;quot;) then --items&lt;br /&gt;
		p.tableMerge(set_categories_in_wikitext, json_res_store.res[p.keys.category]) -- categories from schema type&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Todo: Consider moving the category and this block to p.getSemanticProperties with store=true. However, settings categories with @category is only possible for subobjects&lt;br /&gt;
	if (smw_res ~= nil) then&lt;br /&gt;
		local display_label = p.getDisplayLabel(jsondata, smw_res.properties)&lt;br /&gt;
		if title.nsText == &amp;quot;Property&amp;quot; then display_label = p.defaultArgPath(jsondata, {p.keys.name}, display_label) end&lt;br /&gt;
&lt;br /&gt;
		if (debug) then msg = msg .. &amp;quot;Store page properties&amp;quot; end&lt;br /&gt;
		-- category handling&lt;br /&gt;
		p.tableMerge(set_categories_in_wikitext, smw_res.properties[p.keys.category_pseudoproperty]) &lt;br /&gt;
		smw_res.properties[p.keys.category_pseudoproperty] = nil -- delete pseudo property&lt;br /&gt;
		&lt;br /&gt;
		smw_res.properties[&#039;HasOswId&#039;] = mw.title.getCurrentTitle().fullText  --set special property OswId to own title&lt;br /&gt;
		&lt;br /&gt;
		-- label and display title handling&lt;br /&gt;
		if display_label ~= nil then &lt;br /&gt;
			smw_res.properties[&#039;Display title of&#039;] = display_label --set special property display title&lt;br /&gt;
			smw_res.properties[&#039;Display title of lowercase&#039;] = display_label:lower() --store lowercase for case insensitive query&lt;br /&gt;
			smw_res.properties[&#039;Display title of normalized&#039;] = display_label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;) --store with all non-alphanumeric chars removed for normalized query&lt;br /&gt;
		end&lt;br /&gt;
		p.setNormalizedLabel(smw_res.properties) --build normalized multilang label&lt;br /&gt;
		mw.ext.displaytitle.set(display_label)&lt;br /&gt;
		--smw_res.properties[&#039;@category&#039;] = jsondata[p.keys.category]&lt;br /&gt;
		local store_res = mw.smw.set( smw_res.properties ) --store as semantic properties&lt;br /&gt;
		if (debug) then msg = msg .. mw.dumpObject(smw_res.properties) end&lt;br /&gt;
		if (store_res) then &lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;SMW SUCCESS: &amp;quot; end&lt;br /&gt;
		else&lt;br /&gt;
			wikitext = wikitext .. store_res.error &lt;br /&gt;
			if (debug) then msg = msg .. &amp;quot;SMW ERROR: &amp;quot; .. store_res.error end&lt;br /&gt;
		end&lt;br /&gt;
		--wikitext = mw.dumpObject(smw_res.properties) .. wikitext&lt;br /&gt;
	end&lt;br /&gt;
	wikitext = wikitext .. &amp;quot;\n&amp;quot; .. p.setCategories({categories=set_categories_in_wikitext, sortkey=display_label}).wikitext&lt;br /&gt;
	&lt;br /&gt;
	if (debug) then mw.logObject(res) end&lt;br /&gt;
	return {wikitext=wikitext, debug_msg=msg}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- renders a default infobox&lt;br /&gt;
-- test: mw.logObject(p.renderInfoBox({jsonschema=p.loadJson({title=&amp;quot;JsonSchema:Entity&amp;quot;}).json, jsondata={uuid=&amp;quot;123123&amp;quot;}}))&lt;br /&gt;
function p.renderInfoBox(args)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, nil) -- local schema from the perspective of the current category&lt;br /&gt;
	local schema_allOfMerged = p.defaultArg(args.schema_allOfMerged, schema) -- global schema with allOfs merged&lt;br /&gt;
	local property_definitions = p.defaultArg(args.property_definitions, {}) -- dict schema_key: {property: &amp;lt;smw_property&amp;gt;, ...}&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	if schema == nil then return res end&lt;br /&gt;
	&lt;br /&gt;
	local context = p.defaultArg(args.context, p.buildContext({jsonschema=schema}).context)&lt;br /&gt;
	local ignore_properties = p.defaultArg(args.ignore_properties, {})&lt;br /&gt;
&lt;br /&gt;
	local schema_label = p.renderMultilangValue({jsonschema=schema})&lt;br /&gt;
	&lt;br /&gt;
	-- see also: https://help.fandom.com/wiki/Extension:Scribunto/HTML_Library_usage_notes&lt;br /&gt;
	local tbl = mw.html.create( &#039;table&#039; )&lt;br /&gt;
	tbl&lt;br /&gt;
		:attr( &#039;class&#039;, &#039;info_box&#039; )&lt;br /&gt;
		:tag( &#039;tr&#039; )&lt;br /&gt;
			:tag( &#039;th&#039; )&lt;br /&gt;
				:attr( &#039;class&#039;, &#039;heading&#039; )&lt;br /&gt;
				:attr( &#039;colspan&#039;, &#039;2&#039; )&lt;br /&gt;
				:wikitext( schema_label )&lt;br /&gt;
	for k,v in pairs(jsondata) do&lt;br /&gt;
		if (not ignore_properties[k]) then&lt;br /&gt;
			if (schema[&#039;properties&#039;] ~= nil and schema[&#039;properties&#039;][k] ~= nil and (type(v) ~= &#039;table&#039; or v[1] ~= nil)) then --literal or literal array&lt;br /&gt;
				local def = schema_allOfMerged[&#039;properties&#039;][k]&lt;br /&gt;
				--mw.logObject(def)&lt;br /&gt;
				&lt;br /&gt;
				local label = p.renderMultilangValue({jsonschema=def, default=k})&lt;br /&gt;
				&lt;br /&gt;
				local description = p.renderMultilangValue({jsonschema=def, key=&amp;quot;description&amp;quot;})&lt;br /&gt;
				if (p.tableLength(p.defaultArgPath(property_definitions, {k, &#039;defined_in&#039;}, {})) &amp;gt; 0) then description = description .. &amp;quot;&amp;lt;br&amp;gt;Definition: &amp;quot; end&lt;br /&gt;
				for i, c in pairs(p.defaultArgPath(property_definitions, {k, &#039;defined_in&#039;}, {})) do &lt;br /&gt;
					if (i &amp;gt; 1) then description = description .. &amp;quot;, &amp;quot; end&lt;br /&gt;
					description = description .. &amp;quot;[[:&amp;quot; ..c .. &amp;quot;]]&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
				if (description ~= &amp;quot;&amp;quot;) then description = &amp;quot;{{#info: &amp;quot; .. description .. &amp;quot;|note }}&amp;quot; end -- smw tooltip&lt;br /&gt;
				label = label .. description&lt;br /&gt;
&lt;br /&gt;
				--res = res .. title &amp;quot;: &amp;quot; .. v&lt;br /&gt;
				local cell = tbl:tag( &#039;tr&#039; )&lt;br /&gt;
									:tag( &#039;th&#039; )&lt;br /&gt;
										:wikitext( label )&lt;br /&gt;
										:done()&lt;br /&gt;
									:tag( &#039;td&#039; )&lt;br /&gt;
				if (type(v) == &#039;table&#039;) then&lt;br /&gt;
					for i,e in pairs(v) do &lt;br /&gt;
						if (type(e) ~= &#039;table&#039;) then &lt;br /&gt;
							local p_type = p.defaultArgPath(context, {k, &#039;@type&#039;}, &#039;@value&#039;)&lt;br /&gt;
							if (p_type == &#039;@id&#039; and p.defaultArgPath(def, {&#039;items&#039;, &#039;type&#039;}, &#039;unknown&#039;) == &#039;string&#039; and def[&#039;eval_template&#039;] == nil) then&lt;br /&gt;
								-- auto-link (OSW-)IDs if no eval_template is present&lt;br /&gt;
								e = string.gsub(e, &amp;quot;Category:&amp;quot;, &amp;quot;:Category:&amp;quot;) -- make sure category links work&lt;br /&gt;
								e = string.gsub(e, &amp;quot;File:&amp;quot;, &amp;quot;:File:&amp;quot;) -- do not embedd images but link to them&lt;br /&gt;
								e = &amp;quot;[[&amp;quot; .. e .. &amp;quot;]]&amp;quot; &lt;br /&gt;
							elseif (p_type == &#039;xsd:date&#039;) then -- formate date with user preferences&lt;br /&gt;
								e = &amp;quot;{{#dateformat:&amp;quot; .. e .. &amp;quot;|ymd}}&amp;quot; &lt;br /&gt;
							elseif (p_type == &#039;xsd:dateTime&#039;) then -- formate time with user preferences&lt;br /&gt;
								local smw_property = p.defaultArgPath(property_definitions, {k, &#039;property&#039;})&lt;br /&gt;
								if (smw_property ~= nil) then e = &amp;quot;{{#ask: [[{{FULLPAGENAME}}]]|?&amp;quot; .. smw_property .. &amp;quot;#LOCL#TO= |format=plain |mainlabel=-}}&amp;quot;&lt;br /&gt;
								else &lt;br /&gt;
									local _, _, date, hours, minutes = string.find(e, &amp;quot;(%S+)[T ](%S+)[:](%S+)[:?]&amp;quot;)&lt;br /&gt;
									e = &amp;quot;{{#dateformat:&amp;quot; .. date .. &amp;quot;|ymd}} &amp;quot; .. hours .. &amp;quot;:&amp;quot; .. minutes .. &amp;quot; (UTC)&amp;quot;&lt;br /&gt;
								end&lt;br /&gt;
							elseif (type(v) == &#039;boolean&#039;) then &lt;br /&gt;
								if (v) then v = &amp;quot;&amp;amp;#x2705;&amp;quot; else v = &amp;quot;&amp;amp;#x274C;&amp;quot; end -- green check mark or red cross&lt;br /&gt;
							elseif (def[&#039;eval_template&#039;] == nil and (string.len(e) &amp;gt; 100) and (string.find(e, &amp;quot;{{&amp;quot;) == nil) and (string.find(e, &amp;quot;&amp;lt;/&amp;quot;) == nil) and (string.find(e, &amp;quot;%[%[&amp;quot;) == nil)) then -- no markup, no links&lt;br /&gt;
								e = string.sub(e, 1, 100) .. &amp;quot;...&amp;quot;; -- limit infobox plain text to max 100 chars&lt;br /&gt;
							elseif (debug) then&lt;br /&gt;
								mw.log(&amp;quot;Unformated: &amp;quot; .. k .. &amp;quot; &amp;quot; .. p.defaultArgPath(def, {&#039;items&#039;, &#039;type&#039;}, &#039;unknown&#039;))&lt;br /&gt;
								mw.logObject(def)&lt;br /&gt;
							end&lt;br /&gt;
							cell:wikitext(&amp;quot;\n* &amp;quot; .. e .. &amp;quot;&amp;quot;) &lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					local p_type = p.defaultArgPath(context, {k, &#039;@type&#039;}, &#039;@value&#039;)&lt;br /&gt;
					if (p_type == &#039;@id&#039; and p.defaultArgPath(def, {&#039;type&#039;}, &#039;unknown&#039;) == &#039;string&#039; and def[&#039;eval_template&#039;] == nil) then &lt;br /&gt;
						-- auto-link (OSW-)IDs if no eval_template is present&lt;br /&gt;
						v = string.gsub(v, &amp;quot;Category:&amp;quot;, &amp;quot;:Category:&amp;quot;) -- make sure category links work&lt;br /&gt;
						v = string.gsub(v, &amp;quot;File:&amp;quot;, &amp;quot;:File:&amp;quot;) -- do not embedd images but link to them&lt;br /&gt;
						v = &amp;quot;[[&amp;quot; .. v .. &amp;quot;]]&amp;quot; &lt;br /&gt;
					elseif (p_type == &#039;xsd:date&#039;) then -- formate date &amp;amp; time with user preferences&lt;br /&gt;
						v = &amp;quot;{{#dateformat:&amp;quot; .. v .. &amp;quot;|ymd}}&amp;quot;&lt;br /&gt;
					elseif (p_type == &#039;xsd:dateTime&#039;) then -- formate time with user preferences&lt;br /&gt;
						local smw_property = p.defaultArgPath(property_definitions, {k, &#039;property&#039;})&lt;br /&gt;
						if (smw_property ~= nil) then v = &amp;quot;{{#ask: [[{{FULLPAGENAME}}]]|?&amp;quot; .. smw_property .. &amp;quot;#LOCL#TO= |format=plain |mainlabel=-}}&amp;quot;&lt;br /&gt;
						else &lt;br /&gt;
							local _, _, date, hours, minutes = string.find(v, &amp;quot;(%S+)[T ](%S+)[:](%S+)[:?]&amp;quot;)&lt;br /&gt;
							v = &amp;quot;{{#dateformat:&amp;quot; .. date .. &amp;quot;|ymd}} &amp;quot; .. hours .. &amp;quot;:&amp;quot; .. minutes .. &amp;quot; (UTC)&amp;quot;&lt;br /&gt;
						end&lt;br /&gt;
					elseif (type(v) == &#039;boolean&#039;) then &lt;br /&gt;
						if (v) then v = &amp;quot;&amp;amp;#x2705;&amp;quot; else v = &amp;quot;&amp;amp;#x274C;&amp;quot; end -- green check mark or red cross&lt;br /&gt;
					elseif (def[&#039;eval_template&#039;] == nil and (string.len(v) &amp;gt; 100) and (string.find(v, &amp;quot;{{&amp;quot;) == nil) and (string.find(v, &amp;quot;&amp;lt;/&amp;quot;) == nil) and (string.find(v, &amp;quot;%[%[&amp;quot;) == nil)) then -- no markup, no links&lt;br /&gt;
						v = string.sub(v, 1, 100) .. &amp;quot;...&amp;quot;; -- limit infobox plain text to max 100 chars&lt;br /&gt;
					elseif (debug) then&lt;br /&gt;
						mw.log(&amp;quot;Unformated: &amp;quot; .. k .. &amp;quot; &amp;quot; .. p.defaultArgPath(def, {&#039;type&#039;}, &#039;unknown&#039;))&lt;br /&gt;
						mw.logObject(def)&lt;br /&gt;
					end&lt;br /&gt;
					cell:wikitext(&amp;quot;\n&amp;quot; .. v .. &amp;quot;&amp;quot;)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	res = res .. tostring( tbl )&lt;br /&gt;
	--mw.logObject(res)&lt;br /&gt;
	&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- test&lt;br /&gt;
-- mw.logObject(p.getCategories({jsonschema={allOf={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test?action=raw&amp;amp;slot=jsonschema&amp;quot;}}, includeNamespace=true}))&lt;br /&gt;
-- mw.logObject(p.getCategories({jsonschema={allOf={{[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test?action=raw&amp;amp;slot=jsonschema&amp;quot;}, {[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Test2?action=raw&amp;amp;slot=jsonschema&amp;quot;}}}}))&lt;br /&gt;
function p.getCategories(args)&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local includeNamespace = p.defaultArg(args.includeNamespace, false)&lt;br /&gt;
	&lt;br /&gt;
	local categories = {}&lt;br /&gt;
		local allOf = jsonschema[p.keys.allOf]&lt;br /&gt;
		if (allOf ~= nil) then&lt;br /&gt;
			--properties[&#039;@category&#039;] = {}&lt;br /&gt;
			for k, entry in pairs(allOf) do&lt;br /&gt;
				if type(entry) == &#039;table&#039; then -- &amp;quot;allOf&amp;quot;: [{&amp;quot;$ref&amp;quot;: &amp;quot;/wiki/Category:Test?action=raw&amp;quot;}]&lt;br /&gt;
					for p, v in pairs(entry) do&lt;br /&gt;
						if (p == &#039;$ref&#039;) then&lt;br /&gt;
							for category in v:gmatch(&amp;quot;Category:([^?]+)&amp;quot;) do -- e.g. &amp;quot;/wiki/Category:Test?action=raw&amp;quot;&lt;br /&gt;
								if (includeNamespace) then category = &amp;quot;Category:&amp;quot; .. category end&lt;br /&gt;
							    table.insert(categories, category)&lt;br /&gt;
							end&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				else -- &amp;quot;allOf&amp;quot;: {&amp;quot;$ref&amp;quot;: &amp;quot;/wiki/Category:Test?action=raw&amp;quot;}&lt;br /&gt;
					if (k == &#039;$ref&#039;) then&lt;br /&gt;
						for category in entry:gmatch(&amp;quot;Category:([^?]+)&amp;quot;) do -- e.g. &amp;quot;/wiki/Category:Test?action=raw&amp;quot;&lt;br /&gt;
							if (includeNamespace) then category = &amp;quot;Category:&amp;quot; .. category end&lt;br /&gt;
							table.insert(categories, category)&lt;br /&gt;
						end&lt;br /&gt;
					end	&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end	&lt;br /&gt;
		&lt;br /&gt;
	return {categories=categories}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--sets a list of categories on the page&lt;br /&gt;
--test: mw.logObject(p.setCategories({categories={&amp;quot;Cat1&amp;quot;, &amp;quot;Category:Cat2&amp;quot;}}))&lt;br /&gt;
function p.setCategories(args)&lt;br /&gt;
	local categories = p.defaultArg(args.categories, {})&lt;br /&gt;
	local sortkey = p.defaultArg(args.sortkey, &amp;quot;&amp;quot;)&lt;br /&gt;
	if (sortkey ~= &amp;quot;&amp;quot;) then sortkey = &amp;quot;|&amp;quot; .. sortkey end&lt;br /&gt;
	if (type(categories) ~= &#039;table&#039;) then categories = {categories} end&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	for k, entry in pairs(categories) do&lt;br /&gt;
		res = res .. &amp;quot;[[Category:&amp;quot; .. string.gsub(entry, &amp;quot;Category:&amp;quot;, &amp;quot;&amp;quot;) .. sortkey ..&amp;quot;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[ test&lt;br /&gt;
category = &amp;quot;Category:Entity&amp;quot;&lt;br /&gt;
jsonschema = p.expandJsonRef({json=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json}).json&lt;br /&gt;
mw.logObject(p.buildContext({jsonschema=jsonschema, debug=true}))&lt;br /&gt;
mw.log(mw.text.jsonEncode(p.buildContext({jsonschema=jsonschema, debug=false}).context))&lt;br /&gt;
or&lt;br /&gt;
jsonschema = {&lt;br /&gt;
	[&amp;quot;@context&amp;quot;]={test=&amp;quot;level 0&amp;quot;}, &lt;br /&gt;
	properties={&lt;br /&gt;
		test={&lt;br /&gt;
			type=&amp;quot;object&amp;quot;,&lt;br /&gt;
			[&amp;quot;@context&amp;quot;]={test1=&amp;quot;level 1&amp;quot;}, &lt;br /&gt;
			properties= {&lt;br /&gt;
				test= {&lt;br /&gt;
					type=&amp;quot;array&amp;quot;,&lt;br /&gt;
					items= {&lt;br /&gt;
						type=&amp;quot;object&amp;quot;,&lt;br /&gt;
						[&amp;quot;@context&amp;quot;]={test2=&amp;quot;level 2&amp;quot;}&lt;br /&gt;
					}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
mw.logObject(p.buildContext({jsonschema=jsonschema, debug=true}))&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- constructs a property specific local jsonld context&lt;br /&gt;
function p.buildContext(args)&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	--mw.logObject(schema)&lt;br /&gt;
	local context = p.defaultArg(args.context, schema[p.keys.context])&lt;br /&gt;
	local result = p.defaultArg(args.result, {})&lt;br /&gt;
	if (context ~= nil) then&lt;br /&gt;
		for k,v in pairs(context) do&lt;br /&gt;
			if type(k) == &#039;number&#039; and type(v) == &#039;string&#039; then&lt;br /&gt;
				--table.insert(result, v) --skip context imports&lt;br /&gt;
			elseif (type(v) == &#039;table&#039; and v[1] ~= nil) then --custom addtional mappings, e. g. &amp;quot;type*&amp;quot;: [&amp;quot;Property:HasType&amp;quot;]&lt;br /&gt;
				result[k] = v&lt;br /&gt;
			elseif (type(v) == &#039;table&#039; and v[&#039;@id&#039;] == nil and v[&#039;@reverse&#039;] == nil) then --subcontext&lt;br /&gt;
				p.tableMerge(result, p.buildContext({context=v}).context)&lt;br /&gt;
			else &lt;br /&gt;
				result[k] = v	&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local properties = p.defaultArg(schema.properties, {})&lt;br /&gt;
&lt;br /&gt;
	-- build property context&lt;br /&gt;
	for k,v in pairs(properties) do&lt;br /&gt;
		local subcontext = nil&lt;br /&gt;
		if (p.defaultArgPath(properties, {k, &#039;type&#039;}) == &#039;object&#039;) then&lt;br /&gt;
			--mw.logObject(properties[k])&lt;br /&gt;
			subcontext = p.buildContext({jsonschema=properties[k]}).context&lt;br /&gt;
		elseif (p.defaultArgPath(properties, {k, &#039;items&#039;, &#039;type&#039;}) == &#039;object&#039;) then &lt;br /&gt;
			--mw.logObject(properties[k][&#039;items&#039;])&lt;br /&gt;
			subcontext = p.buildContext({jsonschema=properties[k][&#039;items&#039;]}).context&lt;br /&gt;
		end&lt;br /&gt;
		if (subcontext ~= nil and p.tableLength(subcontext) &amp;gt; 0) then&lt;br /&gt;
			if (result[k] == nil) then result[k] = {} end&lt;br /&gt;
			if (type(result[k]) == &#039;string&#039;) then result[k] = {[&amp;quot;@id&amp;quot;]=result[k]} end&lt;br /&gt;
			if (result[k][p.keys.context] == nil) then result[k][p.keys.context] = {} end&lt;br /&gt;
			result[k][p.keys.context] = p.tableMerge(result[k][p.keys.context], subcontext)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return {context=result}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--maps jsondata values to semantic properties by using the @context attribute within the schema&lt;br /&gt;
--test: mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={test=&amp;quot;Property:schema:TestProperty&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}, jsondata={test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;}, debug=true}))&lt;br /&gt;
--test: mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={&amp;quot;some uri&amp;quot;,{test=&amp;quot;Property:TestProperty&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}}, jsondata={test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;}, debug=true}))&lt;br /&gt;
--[[&lt;br /&gt;
mw.logObject(p.getSemanticProperties({jsonschema={[&amp;quot;@context&amp;quot;]={test=&amp;quot;Property:TestProperty&amp;quot;, subobject=&amp;quot;Property:HasSubobject&amp;quot;, myObjectProperty={[&amp;quot;@id&amp;quot;]= &amp;quot;Property:MyObjectProperty&amp;quot;, [&amp;quot;@type&amp;quot;]= &amp;quot;@id&amp;quot;}}}, jsondata={&lt;br /&gt;
test=&amp;quot;TestValue&amp;quot;, myObjectProperty=&amp;quot;123&amp;quot;, subobject={uuid=&amp;quot;123-123-123&amp;quot;, test=&amp;quot;TestValue2&amp;quot;}&lt;br /&gt;
}, debug=true}))&lt;br /&gt;
&lt;br /&gt;
mw.logObject(p.getSemanticProperties({jsonschema=p.loadJson({title=&amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;, slot=&amp;quot;jsonschema&amp;quot;}).json, p.loadJson({title=&amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;, slot=&amp;quot;jsonsdata&amp;quot;}).json, debug=true}))&lt;br /&gt;
&lt;br /&gt;
category = &amp;quot;Category:Hardware&amp;quot;&lt;br /&gt;
page = &amp;quot;Item:OSW7d7193567ea14e4e89b74de88983b718&amp;quot;&lt;br /&gt;
category2 = &amp;quot;Category:OSW80e240a2e17d4ae5adfe6419051aa0bb&amp;quot;&lt;br /&gt;
page2 = &amp;quot;Item:OSWa4da6664aeac466a86b09e6b32a1cb41&amp;quot;&lt;br /&gt;
jsonschema =p.walkJsonSchema({jsonschema=p.loadJson({title=category, slot=&amp;quot;jsonschema&amp;quot;}).json, debug=true}).jsonschema&lt;br /&gt;
mw.logObject(p.getSemanticProperties({&lt;br /&gt;
	jsonschema=jsonschema,&lt;br /&gt;
	jsondata=p.expandEmbeddedTemplates({jsonschema=jsonschema, jsondata=p.loadJson({title=page, slot=&amp;quot;jsondata&amp;quot;}).json}).res,&lt;br /&gt;
	debug=true&lt;br /&gt;
}).properties)&lt;br /&gt;
&lt;br /&gt;
--]]&lt;br /&gt;
function p.getSemanticProperties(args)&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local subschema = p.defaultArg(args.subschema, schema)&lt;br /&gt;
	local parent_schema_property = p.defaultArg(args.parent_schema_property, {}) -- ToDo: Not used except in getSemanticQuery =&amp;gt; remove&lt;br /&gt;
	local store = p.defaultArg(args.store, false)&lt;br /&gt;
	local root = p.defaultArg(args.root, true)&lt;br /&gt;
	local properties = p.defaultArg(args.properties, {}) --semantic properties to store, dict key=property_name, value=array of string values&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	--if (debug) then mw.logObject(&amp;quot;Call getSemanticProperties with args &amp;quot; .. mw.dumpObject( args ) .. &amp;quot;\n&amp;lt;br&amp;gt;&amp;quot;) end&lt;br /&gt;
	&lt;br /&gt;
	local subjectId = mw.title.getCurrentTitle().fullText&lt;br /&gt;
	local subobjectId = nil&lt;br /&gt;
	if (root == false and jsondata[&#039;uuid&#039;] ~= nil) then &lt;br /&gt;
		subobjectId = &amp;quot;OSW&amp;quot; .. string.gsub(jsondata[&#039;uuid&#039;], &amp;quot;-&amp;quot;, &amp;quot;&amp;quot;) &lt;br /&gt;
		subjectId = subjectId .. &#039;#&#039; .. subobjectId&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local property_data = {}&lt;br /&gt;
	local context = p.defaultArg(args.context, p.buildContext({jsonschema=schema}).context)&lt;br /&gt;
	local error = &amp;quot;&amp;quot;&lt;br /&gt;
	if (debug) then mw.logObject(context) end&lt;br /&gt;
	if schema ~= nil and context ~= nil then&lt;br /&gt;
		local schema_properties = p.defaultArg(subschema.properties, {})&lt;br /&gt;
		if (debug and root) then&lt;br /&gt;
			for k,v in pairs(context) do&lt;br /&gt;
				if type(k) == &#039;number&#039; then mw.logObject(&amp;quot;imports &amp;quot; .. v)&lt;br /&gt;
				elseif type(v) == &#039;table&#039; and v[&amp;quot;@id&amp;quot;] ~= nil then mw.logObject(&amp;quot;&amp;quot; .. k .. &amp;quot; maps to &amp;quot; .. v[&amp;quot;@id&amp;quot;]) &lt;br /&gt;
				else mw.logObject(&amp;quot;&amp;quot; .. k .. &amp;quot; maps to &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		for k,v in pairs(jsondata) do&lt;br /&gt;
			local property_names = {}&lt;br /&gt;
			local subobject_properties = {} -- reverse properties to store in the subobject&lt;br /&gt;
			local mapping_found = false&lt;br /&gt;
			local property_definitions = {} -- list of objects {id=..., reverse=...}&lt;br /&gt;
&lt;br /&gt;
			for term, def in pairs(context) do&lt;br /&gt;
				local term_parts = p.splitString(term, &amp;quot;*&amp;quot;)&lt;br /&gt;
				if (term_parts[1] == k) then --custom additional mapping term*(*...): &amp;quot;Property:...&amp;quot;&lt;br /&gt;
					if type(def) == &#039;table&#039; then &lt;br /&gt;
						-- note: json-ld allows only @id OR @reverse&lt;br /&gt;
						if (def[&amp;quot;@id&amp;quot;] ~= nil) then table.insert(property_definitions, {id=def[&amp;quot;@id&amp;quot;], reverse=false}) end&lt;br /&gt;
						if (def[&amp;quot;@reverse&amp;quot;] ~= nil) then table.insert(property_definitions, {id=def[&amp;quot;@reverse&amp;quot;], reverse=true}) end&lt;br /&gt;
					else table.insert(property_definitions, {id=def}) end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if (debug) then mw.logObject(property_definitions) end&lt;br /&gt;
			for i,e in ipairs(property_definitions) do &lt;br /&gt;
				local id = e[&amp;quot;id&amp;quot;]&lt;br /&gt;
				local property_definition = p.splitString(id, &#039;:&#039;)&lt;br /&gt;
				if property_definition[1] == p.keys.property_ns_prefix then&lt;br /&gt;
					mapping_found = true&lt;br /&gt;
					property_name = string.gsub(id, p.keys.property_ns_prefix .. &amp;quot;:&amp;quot;, &amp;quot;&amp;quot;) -- also allow prefix properties like: Property:schema:url&lt;br /&gt;
					if (e[&amp;quot;reverse&amp;quot;]) then -- reverse properties are handled in the respective subobject&lt;br /&gt;
						if (subobject_properties[property_name] == nil) then subobject_properties[property_name] = {} end --initialize empty list&lt;br /&gt;
						table.insert(subobject_properties[property_name], subjectId) -- add triple subobject -property-&amp;gt; subject&lt;br /&gt;
					else table.insert(property_names, property_name) end&lt;br /&gt;
					local schema_property = p.defaultArg(schema_properties[k], {})&lt;br /&gt;
					local schema_type = p.defaultArg(schema_property.type, nil) --todo: also load smw property type on demand&lt;br /&gt;
					property_data[k] = {schema_type=schema_type, schema_data=schema_property, property=property_name, value=v, reverse=e[&amp;quot;reverse&amp;quot;]}&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			for i, property_name in ipairs(property_names) do&lt;br /&gt;
				if (properties[property_name] == nil) then properties[property_name] = {} end --initialize empty list&lt;br /&gt;
			end&lt;br /&gt;
			if type(v) == &#039;table&#039; then &lt;br /&gt;
				--if (debug) then mw.logObject(&amp;quot;prop &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
				if (mapping_found) then&lt;br /&gt;
					local subcontext = p.copy(p.defaultArgPath(context, {k, p.keys.context}, {})) --deepcopy, see also https://phabricator.wikimedia.org/T269990&lt;br /&gt;
					context = p.tableMerge(context, subcontext) -- pull up nested context&lt;br /&gt;
					local values = {}&lt;br /&gt;
					if (v[1] == nil) then --key value array = object/dict&lt;br /&gt;
						local subproperties_res = p.getSemanticProperties({jsonschema=schema, jsondata=v, properties=p.copy(subobject_properties), store=true, root=false, debug=debug, context=context, subschema=schema_properties[k], parent_schema_property=property_data[k]})&lt;br /&gt;
						local id = subproperties_res.id --subobject_id&lt;br /&gt;
						if (id ~= nil) then &lt;br /&gt;
							id = mw.title.getCurrentTitle().fullText .. &#039;#&#039; .. id&lt;br /&gt;
							table.insert(values, id) &lt;br /&gt;
						end&lt;br /&gt;
						properties = p.processStatement({subject=properties, statement=subproperties_res.properties, debug=debug}).subject&lt;br /&gt;
					else --list array&lt;br /&gt;
						for i, e in pairs(v) do&lt;br /&gt;
							if (type(e) == &#039;table&#039;) then &lt;br /&gt;
								local subproperties_res = p.getSemanticProperties({jsonschema=schema, jsondata=e, properties=p.copy(subobject_properties), store=true, root=false, debug=debug, context=context, subschema=schema_properties[k], parent_schema_property=property_data[k]})&lt;br /&gt;
								local id = subproperties_res.id --subobject_id&lt;br /&gt;
								if (id ~= nil) then &lt;br /&gt;
									id = mw.title.getCurrentTitle().fullText .. &#039;#&#039; .. id&lt;br /&gt;
									table.insert(values, id) &lt;br /&gt;
								end&lt;br /&gt;
								properties = p.processStatement({subject=properties, statement=subproperties_res.properties, debug=debug}).subject&lt;br /&gt;
							else values = v end --plain strings&lt;br /&gt;
						end&lt;br /&gt;
					end &lt;br /&gt;
					for pi, property_name in ipairs(property_names) do&lt;br /&gt;
						for i,value in pairs(values) do table.insert(properties[property_name], value) end&lt;br /&gt;
						if (debug) then mw.logObject(&amp;quot;set &amp;quot; .. property_name .. &amp;quot; = &amp;quot; .. mw.dumpObject(values)) end&lt;br /&gt;
					end&lt;br /&gt;
				else if (debug) then mw.logObject(&amp;quot;not mapped: &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end &lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				if (mapping_found) then &lt;br /&gt;
					for pi, property_name in ipairs(property_names) do&lt;br /&gt;
						table.insert(properties[property_name], v)&lt;br /&gt;
						if (debug) then mw.logObject(&amp;quot;set &amp;quot; .. property_name .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end&lt;br /&gt;
					end&lt;br /&gt;
				else &lt;br /&gt;
					if (debug) then mw.logObject(&amp;quot;not mapped: &amp;quot; .. k .. &amp;quot; = &amp;quot; .. mw.dumpObject(v)) end &lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local store_res = nil&lt;br /&gt;
	if (store) then &lt;br /&gt;
		properties[&#039;HasOswId&#039;] = subjectId&lt;br /&gt;
		if (root) then &lt;br /&gt;
			if (debug) then mw.logObject(&amp;quot;Store page properties&amp;quot;) end&lt;br /&gt;
			store_res = mw.smw.set( properties ) --store as semantic properties&lt;br /&gt;
		else&lt;br /&gt;
			properties[&#039;@category&#039;] = {}&lt;br /&gt;
			p.tableMerge(properties[&#039;@category&#039;], jsondata[p.keys.category]) -- from json property &#039;type&#039;&lt;br /&gt;
			p.tableMerge(properties[&#039;@category&#039;], properties[p.keys.category_pseudoproperty]) -- from json-ld context &#039;Property:Category&#039;&lt;br /&gt;
			properties[p.keys.category_pseudoproperty] = nil -- delete pseudo property&lt;br /&gt;
			&lt;br /&gt;
			local display_label = p.getDisplayLabel(jsondata, properties)&lt;br /&gt;
			if properties[&#039;Display title of&#039;] == nil and properties[&#039;Display_title_of&#039;] == nil then&lt;br /&gt;
				if (display_label ~= nil and display_label ~= &amp;quot;&amp;quot;) then properties[&#039;Display title of&#039;] = display_label&lt;br /&gt;
				else properties[&#039;Display title of&#039;] = p.defaultArg(subschema[&#039;title&#039;], &amp;quot;&amp;quot;) end -- fall back to property name in schema&lt;br /&gt;
			end&lt;br /&gt;
			p.setNormalizedLabel(properties) --build normalized multilang label&lt;br /&gt;
			if (p.tableLength(properties) &amp;gt; 0) then&lt;br /&gt;
				store_res = mw.smw.subobject( properties, subobjectId )	--store as subobject&lt;br /&gt;
				if (debug) then mw.logObject(&amp;quot;Store subobject with id &amp;quot; .. (subobjectId or &amp;quot;&amp;lt;random&amp;gt;&amp;quot;)) end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then mw.logObject(properties) end&lt;br /&gt;
	if (store_res ~= nil) then &lt;br /&gt;
		if (not store_res and store_res.error ~= nil) then error = error .. store_res.error end&lt;br /&gt;
	end&lt;br /&gt;
	if (debug) then mw.logObject(error) end&lt;br /&gt;
	return {properties=properties, definitions=property_data, id=subobjectId, context=context, error=error}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.processStatement(args)&lt;br /&gt;
	local statement = p.defaultArg(args.statement)&lt;br /&gt;
	local subject = p.defaultArg(args.subject)&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
&lt;br /&gt;
	-- handle &amp;quot;approved&amp;quot; statements&lt;br /&gt;
	if (statement[&amp;quot;HasSubject&amp;quot;] == nil or statement[&amp;quot;HasSubject&amp;quot;][1] == nil or statement[&amp;quot;HasSubject&amp;quot;][1] == &amp;quot;&amp;quot;) then --implicit subject&lt;br /&gt;
		if (statement[&amp;quot;HasProperty&amp;quot;] ~= nil and statement[&amp;quot;HasProperty&amp;quot;][1] ~= nil and statement[&amp;quot;HasProperty&amp;quot;][1] ~= &amp;quot;&amp;quot; and statement[&amp;quot;HasObject&amp;quot;] ~= nil) then&lt;br /&gt;
			local property = string.gsub(statement[&amp;quot;HasProperty&amp;quot;][1], p.keys.property_ns_prefix .. &amp;quot;:&amp;quot;, &amp;quot;&amp;quot;) -- also allow prefix properties like: Property:schema:url&lt;br /&gt;
			if (debug) then&lt;br /&gt;
				mw.log(&amp;quot;Set property &amp;quot; .. property .. &amp;quot; from statement to &amp;quot;)&lt;br /&gt;
				mw.logObject(statement[&amp;quot;HasObject&amp;quot;])&lt;br /&gt;
			end&lt;br /&gt;
			if (subject[property] == nil) then subject[property] = {} end&lt;br /&gt;
			for k, v in pairs(statement[&amp;quot;HasObject&amp;quot;]) do table.insert(subject[property], v) end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return {subject=subject}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- build a semantic query based on provided properties and their schema definition&lt;br /&gt;
--[[ test: &lt;br /&gt;
mw.logObject(p.getSemanticQuery({&lt;br /&gt;
	jsonschema={&lt;br /&gt;
		[&amp;quot;@context&amp;quot;]={&lt;br /&gt;
			test=&amp;quot;Property:TestProperty&amp;quot;,&lt;br /&gt;
			number_max=&amp;quot;Property:HasNumber&amp;quot;,&lt;br /&gt;
			date_min=&amp;quot;Property:HasDate&amp;quot;&lt;br /&gt;
		}, &lt;br /&gt;
		properties={&lt;br /&gt;
			test={title=&amp;quot;Test&amp;quot;, type=&amp;quot;string&amp;quot;},&lt;br /&gt;
			number_max={title=&amp;quot;Number&amp;quot;, type=&amp;quot;string&amp;quot;, format=&amp;quot;number&amp;quot;, options={role={query={filter=&amp;quot;max&amp;quot;}}}},&lt;br /&gt;
			date_min={title=&amp;quot;Date&amp;quot;, type=&amp;quot;string&amp;quot;, format=&amp;quot;date&amp;quot;, options={role={query={filter=&amp;quot;min&amp;quot;}}}},&lt;br /&gt;
		}&lt;br /&gt;
	}, &lt;br /&gt;
	jsondata={test=&amp;quot;TestValue&amp;quot;, number_max=5, date_min=&amp;quot;01.01.2023&amp;quot;}&lt;br /&gt;
}))&lt;br /&gt;
--]]&lt;br /&gt;
function p.getSemanticQuery(args)&lt;br /&gt;
	--local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	--local schema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local res = &amp;quot;&amp;quot;&lt;br /&gt;
	local where = &amp;quot;&amp;quot;&lt;br /&gt;
	local select = &amp;quot;&amp;quot;&lt;br /&gt;
	local semantic_properties = p.getSemanticProperties(args)&lt;br /&gt;
	--mw.logObject(semantic_properties)&lt;br /&gt;
	for k,def in pairs(semantic_properties.definitions) do&lt;br /&gt;
		-- see also: https://www.semantic-mediawiki.org/wiki/Help:Search_operators&lt;br /&gt;
		local filter = p.defaultArgPath(def.schema_data, {&#039;options&#039;, &#039;role&#039;, &#039;query&#039;, &#039;filter&#039;}, &#039;eq&#039;)&lt;br /&gt;
		local value = def.value&lt;br /&gt;
		if def.schema_data.type == &#039;string&#039; and (def.schema_data.format == &#039;number&#039; or def.schema_data.format == &#039;date&#039;) then &lt;br /&gt;
			if (filter == &#039;min&#039;) then value = &amp;quot;&amp;lt;&amp;quot; .. value&lt;br /&gt;
			elseif (filter == &#039;max&#039;) then value = &amp;quot;&amp;gt;&amp;quot; .. value&lt;br /&gt;
			else value = value end --exact match&lt;br /&gt;
		elseif def.schema_data.type == &#039;string&#039; then&lt;br /&gt;
			value = &amp;quot;~*&amp;quot; .. value .. &amp;quot;*&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		where = where .. &amp;quot;\n[[&amp;quot;.. def.property .. &amp;quot;::&amp;quot; .. value .. &amp;quot;]]&amp;quot;&lt;br /&gt;
		select = select .. &amp;quot;\n|?&amp;quot; .. def.property&lt;br /&gt;
		if (def.schema_data.title ~= nil) then select = select .. &amp;quot;=&amp;quot; .. def.schema_data.title end&lt;br /&gt;
	end&lt;br /&gt;
	if (where ~= &amp;quot;&amp;quot;) then res = &amp;quot;{{#ask:&amp;quot; .. res .. where .. select .. &amp;quot;}}&amp;quot; end&lt;br /&gt;
	return {wikitext=res}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- HELPERS&lt;br /&gt;
&lt;br /&gt;
-- expands all $ref&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={items={test=&amp;quot;value&amp;quot;, [&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/JsonSchema:Label?action=raw&amp;quot;}}}).json)&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/Category:Item?action=raw&amp;amp;slot=jsonschema&amp;quot;}}).json)&lt;br /&gt;
--test: mw.logObject(p.expandJsonRef({json={[&amp;quot;$ref&amp;quot;]=&amp;quot;/wiki/JsonSchema:Statement?action=raw&amp;quot;}}).json)&lt;br /&gt;
function p.expandJsonRef(args)&lt;br /&gt;
	local json = p.defaultArg(args.json, {})&lt;br /&gt;
	local debug = p.defaultArg(args.debug, false)&lt;br /&gt;
	local refs = {}&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if (k == &amp;quot;$ref&amp;quot;) then&lt;br /&gt;
    		-- e. g. &amp;quot;/wiki/JsonSchema:Label?action=raw&amp;quot; or &amp;quot;/wiki/Category:Entity?action=raw&amp;amp;slot=jsonschema&amp;quot;&lt;br /&gt;
    		if string.find(v, &amp;quot;#&amp;quot;) then&lt;br /&gt;
    			if (debug) then mw.logObject(&amp;quot;Skip relative reference&amp;quot;) end&lt;br /&gt;
    		else&lt;br /&gt;
	    		local uri = mw.uri.new(v)&lt;br /&gt;
	    		local ref_title = mw.text.split(uri.path, &amp;quot;wiki/&amp;quot;, true)[2]&lt;br /&gt;
	    		local ref_slot = uri.query[&amp;quot;slot&amp;quot;]&lt;br /&gt;
	    		if (debug) then &lt;br /&gt;
		    		if (ref_slot ~= nil) then mw.logObject(&amp;quot;Ref found with title &amp;quot; .. ref_title .. &amp;quot; and slot &amp;quot; .. ref_slot)&lt;br /&gt;
		    		else mw.logObject(&amp;quot;Ref found with title &amp;quot; .. ref_title) end&lt;br /&gt;
	    		end&lt;br /&gt;
	    		local ref_json = p.loadJson({title=ref_title, slot=ref_slot}).json&lt;br /&gt;
	    		refs[v] = ref_json&lt;br /&gt;
	    		json[k] = nil&lt;br /&gt;
    		end&lt;br /&gt;
    	end&lt;br /&gt;
    end&lt;br /&gt;
	--mw.logObject(refs)&lt;br /&gt;
	for k,v in pairs(refs) do&lt;br /&gt;
		json = p.tableMerge(v, json)&lt;br /&gt;
	end&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
            json[k] = p.expandJsonRef({json=v}).json&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    local result = p.copy(json)&lt;br /&gt;
    for k,v in pairs(json) do&lt;br /&gt;
    	if (k == &amp;quot;allOf&amp;quot;) then&lt;br /&gt;
            if (type(v) == &amp;quot;table&amp;quot; and v[1] == nil) then v = {v} end -- ensure array&lt;br /&gt;
            for i,s in pairs(v) do &lt;br /&gt;
            	result = p.tableMerge(s, result)&lt;br /&gt;
            	if (debug) then mw.log(&amp;quot;merge allOf with title &amp;quot; .. s[&amp;quot;title&amp;quot;]) end&lt;br /&gt;
            end&lt;br /&gt;
            result[k] = nil&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    &lt;br /&gt;
    return {json=result}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function p.defaultArg(arg, default)&lt;br /&gt;
	if (arg == nil) then &lt;br /&gt;
		return default&lt;br /&gt;
	else&lt;br /&gt;
		return arg&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- returns the value of a table (dict) path or default, if the path is not defined&lt;br /&gt;
-- test: mw.logObject(p.defaultArgPath({some={defined={path=&amp;quot;value&amp;quot;}}}, {&amp;quot;some&amp;quot;, &amp;quot;defined&amp;quot;, &amp;quot;path&amp;quot;}, &amp;quot;default_value&amp;quot;))&lt;br /&gt;
-- test: mw.logObject(p.defaultArgPath({some={defined={path=&amp;quot;value&amp;quot;}}}, {&amp;quot;some&amp;quot;, &amp;quot;undefined&amp;quot;, &amp;quot;path&amp;quot;}, &amp;quot;default_value&amp;quot;))&lt;br /&gt;
function p.defaultArgPath(arg, path, default)&lt;br /&gt;
	if (arg == nil) then &lt;br /&gt;
		return default&lt;br /&gt;
	elseif (path == nil) then&lt;br /&gt;
		return arg&lt;br /&gt;
	else&lt;br /&gt;
		key = table.remove(path,1)&lt;br /&gt;
		if (key == nil) then return arg end  --end of path&lt;br /&gt;
		return p.defaultArgPath(arg[key], path, default)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.splitString(inputstr, sep)&lt;br /&gt;
	&lt;br /&gt;
        if sep == nil then&lt;br /&gt;
                sep = &amp;quot;;&amp;quot;&lt;br /&gt;
        end&lt;br /&gt;
        local t={}&lt;br /&gt;
        for str in string.gmatch(inputstr, &amp;quot;([^&amp;quot;..sep..&amp;quot;]+)&amp;quot;) do&lt;br /&gt;
                table.insert(t, str)&lt;br /&gt;
        end&lt;br /&gt;
        return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--dumps a table to a string (replaced by mw.dumpObject())&lt;br /&gt;
function p.dump(o)&lt;br /&gt;
   return mw.dumpObject(o)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--converts a literal to an table&lt;br /&gt;
function p.tablefy(o)&lt;br /&gt;
	if (o == nil) then o = {} end&lt;br /&gt;
	if (type(o) ~= &#039;table&#039;) then o = {o} end&lt;br /&gt;
	return o&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--true if the value is contained in the array (flat arrays only)&lt;br /&gt;
function p.tableContains (tab, val)&lt;br /&gt;
    for index, value in ipairs(tab) do&lt;br /&gt;
        if value == val then&lt;br /&gt;
            return true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--get the size of a table&lt;br /&gt;
function p.tableLength(t)&lt;br /&gt;
  local count = 0&lt;br /&gt;
  for _ in pairs(t) do count = count + 1 end&lt;br /&gt;
  return count&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--check if a variable is nil or an empty string or table&lt;br /&gt;
function p.nilOrEmpty(o)&lt;br /&gt;
	if (o == nil) then return true&lt;br /&gt;
	elseif (type(o) == &#039;string&#039; and o == &amp;quot;&amp;quot;) then return true&lt;br /&gt;
	elseif (type(o) == &#039;table&#039; and p.tableLength(o) == 0) then return true&lt;br /&gt;
	else return false &lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- merges t2 to t1&lt;br /&gt;
--test: mw.logObject(p.tableMerge({&amp;quot;string&amp;quot;, test1=&amp;quot;test1&amp;quot;, subtable1={&amp;quot;test&amp;quot;}}, {&amp;quot;string2&amp;quot;, test1=&amp;quot;test2&amp;quot;, test3=&amp;quot;test4&amp;quot;}))&lt;br /&gt;
function p.tableMerge(t1, t2)&lt;br /&gt;
	if (t1 == nil) then t1 = {} elseif (type(t1) ~= &#039;table&#039;) then t1 = {t1} end&lt;br /&gt;
	if (t2 == nil) then t2 = {} elseif (type(t2) ~= &#039;table&#039;) then t2 = {t2} end&lt;br /&gt;
    for k,v in pairs(t2) do&lt;br /&gt;
        if type(v) == &amp;quot;table&amp;quot; then&lt;br /&gt;
            if type(t1[k] or false) == &amp;quot;table&amp;quot; then&lt;br /&gt;
                p.tableMerge(t1[k] or {}, t2[k] or {})&lt;br /&gt;
            else&lt;br /&gt;
                if type(k) == &#039;number&#039; then table.insert(t1, v)&lt;br /&gt;
            	else t1[k] = v end&lt;br /&gt;
            end&lt;br /&gt;
        else&lt;br /&gt;
        	if type(k) == &#039;number&#039; then table.insert(t1, v)&lt;br /&gt;
            else t1[k] = v end&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return t1&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- from https://stackoverflow.com/questions/640642/how-do-you-copy-a-lua-table-by-value&lt;br /&gt;
function p.copy(obj, seen)&lt;br /&gt;
  if type(obj) ~= &#039;table&#039; then return obj end&lt;br /&gt;
  if seen and seen[obj] then return seen[obj] end&lt;br /&gt;
  local s = seen or {}&lt;br /&gt;
  local res = setmetatable({}, getmetatable(obj))&lt;br /&gt;
  s[obj] = res&lt;br /&gt;
  for k, v in pairs(obj) do res[p.copy(k, s)] = p.copy(v, s) end&lt;br /&gt;
  return res&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- get normalized label&lt;br /&gt;
function p.getDisplayLabel(jsondata, properties)&lt;br /&gt;
	local display_label = nil&lt;br /&gt;
	-- check if label properties are mapped&lt;br /&gt;
	if (properties[&amp;quot;HasLabel&amp;quot;] ~= nil and properties[&amp;quot;HasLabel&amp;quot;][1] ~= nil) then display_label = p.splitString(properties[&amp;quot;HasLabel&amp;quot;][1], &#039;@&#039;)[1] &lt;br /&gt;
	elseif (properties[&amp;quot;HasName&amp;quot;] ~= nil) then &lt;br /&gt;
		if type(properties[&amp;quot;HasName&amp;quot;]) == &#039;table&#039; then display_label = properties[&amp;quot;HasName&amp;quot;][1]&lt;br /&gt;
		else display_label = properties[&amp;quot;HasName&amp;quot;] end&lt;br /&gt;
	-- fall back to unmapped keywords&lt;br /&gt;
	elseif (jsondata[p.keys.label] ~= nil and jsondata[p.keys.label][1] ~= nil) then display_label = p.splitString(jsondata[p.keys.label][1], &#039;@&#039;)[1] &lt;br /&gt;
	elseif (jsondata[p.keys.name] ~= nil) then display_label = jsondata[p.keys.name] &lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return display_label&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- build normalized multilang label&lt;br /&gt;
function p.setNormalizedLabel(properties, use_fallbacks)&lt;br /&gt;
	if (use_fallbacks == nil) then use_fallbacks = true end&lt;br /&gt;
	if (properties[&#039;HasLabel&#039;] ~= nil) then &lt;br /&gt;
		labels = properties[&#039;HasLabel&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = p.splitString(label, &#039;@&#039;)[1]:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			label_lang = &amp;quot;en&amp;quot;&lt;br /&gt;
			if (p.splitString(label, &#039;@&#039;)[2] ~= nil) then label_lang = p.splitString(label, &#039;@&#039;)[2] end&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@&amp;quot; .. label_lang)	&lt;br /&gt;
		end&lt;br /&gt;
	&lt;br /&gt;
	elseif (use_fallbacks and properties[&#039;HasName&#039;] ~= nil) then -- fallback, assume English lang&lt;br /&gt;
		labels = properties[&#039;HasName&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@en&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	elseif (use_fallbacks and properties[&#039;Display title of&#039;] ~= nil) then -- fallback, assume English lang&lt;br /&gt;
		labels = properties[&#039;Display title of&#039;]&lt;br /&gt;
		if(type(labels) ~= &#039;table&#039;) then labels = {labels} end&lt;br /&gt;
		properties[&#039;HasNormalizedLabel&#039;] = {}&lt;br /&gt;
		for i, label in ipairs(labels) do&lt;br /&gt;
			label_norm = label:lower():gsub(&#039;[^%w]+&#039;,&#039;&#039;)&lt;br /&gt;
			table.insert(properties[&#039;HasNormalizedLabel&#039;], label_norm .. &amp;quot;@en&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderMultilangValue(args)&lt;br /&gt;
	local jsondata = p.defaultArg(args.jsondata, {})&lt;br /&gt;
	local jsonschema = p.defaultArg(args.jsonschema, {})&lt;br /&gt;
	local key = p.defaultArg(args.key, &amp;quot;title&amp;quot;)&lt;br /&gt;
	local result = p.defaultArg(args.default, &amp;quot;&amp;quot;)&lt;br /&gt;
	local default = p.defaultArg(args.default, nil)&lt;br /&gt;
	-- &amp;quot;title*&amp;quot;: {&amp;quot;de&amp;quot;: ...}&lt;br /&gt;
	if jsonschema[key] ~= nil then result = jsonschema[key] end&lt;br /&gt;
	if jsonschema[key .. &#039;*&#039;] ~= nil then -- multilang label with switch&lt;br /&gt;
		result = &amp;quot;{{#switch:{{USERLANGUAGECODE}} |#default=&amp;quot; ..  result&lt;br /&gt;
		for k,v in pairs(jsonschema[key .. &#039;*&#039;]) do &lt;br /&gt;
			if k == en then default = v &lt;br /&gt;
			else result = result .. &amp;quot; |&amp;quot; .. k .. &amp;quot;=&amp;quot; .. v end &lt;br /&gt;
		end&lt;br /&gt;
		if default ~= nil then result = result .. &amp;quot; |#default=&amp;quot; ..  default end&lt;br /&gt;
		result = result .. &amp;quot; }}&amp;quot;&lt;br /&gt;
	end	&lt;br /&gt;
	-- &amp;quot;some_property&amp;quot;: [{&amp;quot;lang&amp;quot;: &amp;quot;de&amp;quot;, &amp;quot;text&amp;quot;: ...}]&lt;br /&gt;
	if jsondata[key] ~= nil then -- multilang label with switch&lt;br /&gt;
		result = &amp;quot;{{#switch:{{USERLANGUAGECODE}}&amp;quot;&lt;br /&gt;
		for k,v in pairs(jsondata[key]) do &lt;br /&gt;
			if v[&amp;quot;lang&amp;quot;] ~= nil and v[&amp;quot;text&amp;quot;] ~= nil then&lt;br /&gt;
				if v[&amp;quot;lang&amp;quot;] == &amp;quot;en&amp;quot; then default = v[&amp;quot;text&amp;quot;]&lt;br /&gt;
				else result = result .. &amp;quot; |&amp;quot; .. v[&amp;quot;lang&amp;quot;] .. &amp;quot;=&amp;quot; .. v[&amp;quot;text&amp;quot;] end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if default ~= nil then result = result .. &amp;quot; |#default=&amp;quot; ..  default end&lt;br /&gt;
		result = result .. &amp;quot; }}&amp;quot;&lt;br /&gt;
	end		&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWe427aafafbac4262955b9f690a83405d&amp;diff=4061</id>
		<title>Category:OSWe427aafafbac4262955b9f690a83405d</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWe427aafafbac4262955b9f690a83405d&amp;diff=4061"/>
		<updated>2025-02-21T06:57:26Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Core&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW5c7c84f0bfbe4347ba7fbe7b346fd106&amp;diff=4060</id>
		<title>Category:OSW5c7c84f0bfbe4347ba7fbe7b346fd106</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW5c7c84f0bfbe4347ba7fbe7b346fd106&amp;diff=4060"/>
		<updated>2025-02-21T06:57:22Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=={{{_label_attachments| {{#switch: {{USERLANGUAGECODE}} |en=Instances|de=Instanzen|#default=Instances}} }}}==&lt;br /&gt;
{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasStartDate={{#switch: {{USERLANGUAGECODE}} |en=Date &amp;amp; time|de=Datum &amp;amp; Uhrzeit|#default=Date &amp;amp; time}}&lt;br /&gt;
|sort=HasStartDate&lt;br /&gt;
|format=datatable&lt;br /&gt;
|limit=1000&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW288260cd0728420c9f40ae1c5fa19111&amp;diff=4059</id>
		<title>Category:OSW288260cd0728420c9f40ae1c5fa19111</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW288260cd0728420c9f40ae1c5fa19111&amp;diff=4059"/>
		<updated>2025-02-21T06:57:08Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Install package: OSW Ontology&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=JsonSchema:PrefectWorkflowRuns&amp;diff=4058</id>
		<title>JsonSchema:PrefectWorkflowRuns</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=JsonSchema:PrefectWorkflowRuns&amp;diff=4058"/>
		<updated>2025-02-21T06:57:03Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;array&amp;quot;,&lt;br /&gt;
    &amp;quot;title&amp;quot;: &amp;quot;Workflow Runs&amp;quot;,&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;Workflows sheduled for this item&amp;quot;,&lt;br /&gt;
    &amp;quot;$comment&amp;quot;: &amp;quot;default prefect workflows with only a subject as parameter&amp;quot;,&lt;br /&gt;
    &amp;quot;eval_template&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;type&amp;quot;: &amp;quot;mustache-wikitext&amp;quot;,&lt;br /&gt;
            &amp;quot;mode&amp;quot;: &amp;quot;render&amp;quot;,&lt;br /&gt;
            &amp;quot;value&amp;quot;: &amp;quot;{{#workflow_runs}} \u003Cdiv class=&#039;PrefectStatusBadge&#039; data-config=&#039;{\&amp;quot;uuid\&amp;quot;:\&amp;quot;{{{uuid}}}\&amp;quot;, {{=\u003C% %\u003E=}} \&amp;quot;parent\&amp;quot;: \&amp;quot;{{FULLPAGENAME}}\&amp;quot;, \&amp;quot;parameters\&amp;quot;: {\&amp;quot;request\&amp;quot;: {\&amp;quot;subject\&amp;quot;:  \&amp;quot;{{FULLPAGENAME}}\&amp;quot; \u003C%={{ }}=%\u003E } } }&#039;\u003E\u003C/div\u003E {{/workflow_runs}}&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;items&amp;quot;: {&lt;br /&gt;
        &amp;quot;_$ref&amp;quot;: &amp;quot;/wiki/Category:OSWb79812225c7849b78e98f2b3b10498b3?action=raw\u0026slot=jsonschema&amp;quot;,&lt;br /&gt;
        &amp;quot;title&amp;quot;: &amp;quot;Workflow Run&amp;quot;,&lt;br /&gt;
        &amp;quot;required&amp;quot;: [&lt;br /&gt;
            &amp;quot;uuid&amp;quot;,&lt;br /&gt;
            &amp;quot;tool&amp;quot;&lt;br /&gt;
        ],&lt;br /&gt;
        &amp;quot;properties&amp;quot;: {&lt;br /&gt;
            &amp;quot;uuid&amp;quot;: {&lt;br /&gt;
                &amp;quot;title&amp;quot;: &amp;quot;UUID&amp;quot;,&lt;br /&gt;
                &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
                &amp;quot;format&amp;quot;: &amp;quot;uuid&amp;quot;,&lt;br /&gt;
                &amp;quot;options&amp;quot;: {&lt;br /&gt;
                    &amp;quot;hidden&amp;quot;: true&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;name&amp;quot;: {&lt;br /&gt;
                &amp;quot;title&amp;quot;: &amp;quot;Name&amp;quot;,&lt;br /&gt;
                &amp;quot;description&amp;quot;: &amp;quot;Technical / Machine compatible name&amp;quot;,&lt;br /&gt;
                &amp;quot;description*&amp;quot;: {&lt;br /&gt;
                    &amp;quot;de&amp;quot;: &amp;quot;Technischer / Maschinenkompatibler Name&amp;quot;&lt;br /&gt;
                },&lt;br /&gt;
                &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
                &amp;quot;options&amp;quot;: {&lt;br /&gt;
                    &amp;quot;hidden&amp;quot;: true&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;label&amp;quot;: {&lt;br /&gt;
                &amp;quot;type&amp;quot;: &amp;quot;array&amp;quot;,&lt;br /&gt;
                &amp;quot;title&amp;quot;: &amp;quot;Labels&amp;quot;,&lt;br /&gt;
                &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                    &amp;quot;de&amp;quot;: &amp;quot;Namen&amp;quot;&lt;br /&gt;
                },&lt;br /&gt;
                &amp;quot;description&amp;quot;: &amp;quot;Human readable names. You have to assign at least one.&amp;quot;,&lt;br /&gt;
                &amp;quot;description*&amp;quot;: {&lt;br /&gt;
                    &amp;quot;de&amp;quot;: &amp;quot;Menschenlesbarer Namen. Du musst mindestens einen vergeben.&amp;quot;&lt;br /&gt;
                },&lt;br /&gt;
                &amp;quot;format&amp;quot;: &amp;quot;table&amp;quot;,&lt;br /&gt;
                &amp;quot;eval_template&amp;quot;: [&lt;br /&gt;
                    {&lt;br /&gt;
                        &amp;quot;type&amp;quot;: &amp;quot;mustache-wikitext&amp;quot;,&lt;br /&gt;
                        &amp;quot;mode&amp;quot;: &amp;quot;render&amp;quot;,&lt;br /&gt;
                        &amp;quot;value&amp;quot;: &amp;quot;{{=\u003C% %\u003E=}} {{#switch:{{USERLANGUAGECODE}} \u003C%={{ }}=%\u003E {{#label}} |{{lang}}={{text}} {{/label}} {{=\u003C% %\u003E=}} }}&amp;quot;&lt;br /&gt;
                    }&lt;br /&gt;
                ],&lt;br /&gt;
                &amp;quot;items&amp;quot;: {&lt;br /&gt;
                    &amp;quot;title&amp;quot;: &amp;quot;Label&amp;quot;,&lt;br /&gt;
                    &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                        &amp;quot;de&amp;quot;: &amp;quot;Name&amp;quot;&lt;br /&gt;
                    },&lt;br /&gt;
                    &amp;quot;eval_template&amp;quot;: [&lt;br /&gt;
                        {&lt;br /&gt;
                            &amp;quot;type&amp;quot;: &amp;quot;wikitext&amp;quot;,&lt;br /&gt;
                            &amp;quot;mode&amp;quot;: &amp;quot;store&amp;quot;,&lt;br /&gt;
                            &amp;quot;value&amp;quot;: &amp;quot;{{{text}}}@{{{lang}}}&amp;quot;&lt;br /&gt;
                        }&lt;br /&gt;
                    ],&lt;br /&gt;
                    &amp;quot;$ref&amp;quot;: &amp;quot;/wiki/JsonSchema:Label?action=raw&amp;quot;&lt;br /&gt;
                },&lt;br /&gt;
                &amp;quot;minItems&amp;quot;: 1&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;status&amp;quot;: {&lt;br /&gt;
                &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
                &amp;quot;format&amp;quot;: &amp;quot;autocomplete&amp;quot;,&lt;br /&gt;
                &amp;quot;title&amp;quot;: &amp;quot;Status&amp;quot;,&lt;br /&gt;
                &amp;quot;default&amp;quot;: &amp;quot;Item:OSWaa8d29404288446a9f3ec7afa4e2a512&amp;quot;,&lt;br /&gt;
                &amp;quot;options&amp;quot;: {&lt;br /&gt;
                    &amp;quot;autocomplete&amp;quot;: {&lt;br /&gt;
                        &amp;quot;category&amp;quot;: &amp;quot;Category:OSW9725d7a91bab4f1aa68f423e4e9bfcf4&amp;quot;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;tool&amp;quot;: {&lt;br /&gt;
                &amp;quot;type&amp;quot;: &amp;quot;array&amp;quot;,&lt;br /&gt;
                &amp;quot;title&amp;quot;: &amp;quot;Workflows&amp;quot;,&lt;br /&gt;
                &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                    &amp;quot;de&amp;quot;: &amp;quot;Workflows&amp;quot;&lt;br /&gt;
                },&lt;br /&gt;
                &amp;quot;minItems&amp;quot;: 1,&lt;br /&gt;
                &amp;quot;maxItems&amp;quot;: 1,&lt;br /&gt;
                &amp;quot;items&amp;quot;: {&lt;br /&gt;
                    &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
                    &amp;quot;title&amp;quot;: &amp;quot;Workflow&amp;quot;,&lt;br /&gt;
                    &amp;quot;title*&amp;quot;: {&lt;br /&gt;
                        &amp;quot;de&amp;quot;: &amp;quot;Workflow&amp;quot;&lt;br /&gt;
                    },&lt;br /&gt;
                    &amp;quot;format&amp;quot;: &amp;quot;autocomplete&amp;quot;,&lt;br /&gt;
                    &amp;quot;range&amp;quot;: &amp;quot;Category:OSW77e749fc598341ac8b6d2fff21574058&amp;quot;,&lt;br /&gt;
                    &amp;quot;options&amp;quot;: {&lt;br /&gt;
                        &amp;quot;autocomplete&amp;quot;: {&lt;br /&gt;
                            &amp;quot;$comment&amp;quot;: &amp;quot;Software hosted by a prefect flow&amp;quot;,&lt;br /&gt;
                            &amp;quot;query&amp;quot;: &amp;quot;[[Category:OSW77e749fc598341ac8b6d2fff21574058]][[-Hosts.HasSchema::Category:OSW72eae3c8f41f4a22a94dbc01974ed404]]&amp;quot;&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWb8b6278763d54b0784eea9d3b3d183a4&amp;diff=4057</id>
		<title>Category:OSWb8b6278763d54b0784eea9d3b3d183a4</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWb8b6278763d54b0784eea9d3b3d183a4&amp;diff=4057"/>
		<updated>2025-02-21T06:57:00Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Namespaces ==&lt;br /&gt;
for UUID generation via uuid5&lt;br /&gt;
&lt;br /&gt;
* General (same as UUID of the category page): b8b62787-63d5-4b07-84ee-a9d3b3d183a4&lt;br /&gt;
* FhG: fdd10d54-8474-5957-83a1-2ac4e40723a6&lt;br /&gt;
&lt;br /&gt;
{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasLabel={{#switch: {{USERLANGUAGECODE}} |de=Label (de) |#default=Label (en) }} |+lang={{USERLANGUAGECODE}} &lt;br /&gt;
|?HasOuNumber={{#switch:{{USERLANGUAGECODE}} |en=OU number|de=OE-Nummer |#default=OU number}}&lt;br /&gt;
|?HasManger={{#switch:{{USERLANGUAGECODE}} |en=Manager|de=Leitung |#default=Manager}}&lt;br /&gt;
|?HasDeputyManger={{#switch:{{USERLANGUAGECODE}} |en=Manager deputy|de=Leitung stv |#default=Manager deputy}}&lt;br /&gt;
|?HasSecretary={{#switch:{{USERLANGUAGECODE}} |en=Secretary|de=Sekretariat |#default=Secretary}}&lt;br /&gt;
|?HasSuperordinateOu={{#switch:{{USERLANGUAGECODE}} |en=Super OU|de=Über-OE |#default=Super OU}}&lt;br /&gt;
|?HasNumberOfMembers={{#switch:{{USERLANGUAGECODE}} |en=Number of members|de=Mitarbeitende (Anzahl) |#default=Number of members }}&lt;br /&gt;
|format=datatable&lt;br /&gt;
|limit=5000&lt;br /&gt;
|sort=Display title of&lt;br /&gt;
|order=asc&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=JsonSchema:StartDateTime&amp;diff=4056</id>
		<title>JsonSchema:StartDateTime</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=JsonSchema:StartDateTime&amp;diff=4056"/>
		<updated>2025-02-21T06:56:51Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
    &amp;quot;title&amp;quot;: &amp;quot;Start date and time&amp;quot;,&lt;br /&gt;
    &amp;quot;title*&amp;quot;: {&lt;br /&gt;
        &amp;quot;de&amp;quot;: &amp;quot;Beginn (Datum und Uhrzeit)&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;format&amp;quot;: &amp;quot;datetime-local&amp;quot;,&lt;br /&gt;
    &amp;quot;options&amp;quot;: {&lt;br /&gt;
        &amp;quot;flatpickr&amp;quot;: {&lt;br /&gt;
            &amp;quot;dateFormat&amp;quot;: &amp;quot;Z&amp;quot;,&lt;br /&gt;
            &amp;quot;altInput&amp;quot;: true,&lt;br /&gt;
            &amp;quot;altFormat&amp;quot;: &amp;quot;Y-m-d h:i K&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW44deaa5b806d41a2a88594f562b110e9&amp;diff=4055</id>
		<title>Category:OSW44deaa5b806d41a2a88594f562b110e9</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW44deaa5b806d41a2a88594f562b110e9&amp;diff=4055"/>
		<updated>2025-02-21T06:56:47Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW662db0a2ad0946148422245f84e82f64&amp;diff=4054</id>
		<title>Category:OSW662db0a2ad0946148422245f84e82f64</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW662db0a2ad0946148422245f84e82f64&amp;diff=4054"/>
		<updated>2025-02-21T06:56:43Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Install package: OSW Ontology&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWc5ed0ed1e33c4b31887c67af25a610c1&amp;diff=4053</id>
		<title>Category:OSWc5ed0ed1e33c4b31887c67af25a610c1</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSWc5ed0ed1e33c4b31887c67af25a610c1&amp;diff=4053"/>
		<updated>2025-02-21T06:56:37Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Namespaces ==&lt;br /&gt;
for UUID generation via uuid5&lt;br /&gt;
&lt;br /&gt;
* General (same as UUID of the category page): c5ed0ed1-e33c-4b31-887c-67af25a610c1&lt;br /&gt;
* FhG: 0459e1a8-adbd-5f2d-a80a-70f4bfbb37a3&lt;br /&gt;
&lt;br /&gt;
{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasRoomUsage={{#switch: {{USERLANGUAGECODE}} |en=Function |de=Funktion |#default=Function }}&lt;br /&gt;
|?IsLocatedIn={{#switch: {{USERLANGUAGECODE}} |en=Located in |de=Lage|#default=Located in }}&lt;br /&gt;
|?HasNumberOfOccupants={{#switch: {{USERLANGUAGECODE}} |en=Number of Occupants |de=Belegung (Anzahl) |#default=Number of Occupants }}&lt;br /&gt;
|format=datatable&lt;br /&gt;
|limit=5000&lt;br /&gt;
|sort=Display title of&lt;br /&gt;
|order=asc&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW0e084decca6f48a7b023d6b7b2c1452d&amp;diff=4052</id>
		<title>Category:OSW0e084decca6f48a7b023d6b7b2c1452d</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Category:OSW0e084decca6f48a7b023d6b7b2c1452d&amp;diff=4052"/>
		<updated>2025-02-21T06:56:24Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=={{#switch: {{USERLANGUAGECODE}} |en=Instances|de=Instanzen|#default=Instances}}==&lt;br /&gt;
{{#ask: [[{{FULLPAGENAME}}]]&lt;br /&gt;
|?HasStartDateAndTime={{#switch: {{USERLANGUAGECODE}} |en=Start date|de=Startdatum|#default=Start date}}&lt;br /&gt;
|?HasEndDateAndTime={{#switch: {{USERLANGUAGECODE}} |en=End date|de=Enddatum|#default=End date}}&lt;br /&gt;
|format=datatable&lt;br /&gt;
|limit=1000&lt;br /&gt;
}}&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
	<entry>
		<id>https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW205fc8dcd20646e586969cf4a0aeca65&amp;diff=4051</id>
		<title>Item:OSW205fc8dcd20646e586969cf4a0aeca65</title>
		<link rel="alternate" type="text/html" href="https://battery.knowledge-graph.eu/w/index.php?title=Item:OSW205fc8dcd20646e586969cf4a0aeca65&amp;diff=4051"/>
		<updated>2025-02-21T06:56:14Z</updated>

		<summary type="html">&lt;p&gt;0000-0003-0410-3616: Update package: OSW Base&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>0000-0003-0410-3616</name></author>
	</entry>
</feed>