mapeditor/tiled

Do you want to work on this issue?

You can request for a bounty in order to promote it!

Include the type for custom classes in JSON #3820

ForeverZer0 posted onGitHub

Describe the bug The JSON output for custom classes lacks detailed information about the custom class, and is given simply as a list of key/value pairs. This differs from XML-based format, where they are given as complete property definitions.

Consider the following possible JSON output of a property with a custom class:

{
    "name":"Something",
    "propertytype":"MyClass",
    "type":"class",
    "value": {
        "SomeString":"#ff000000",
        "SomeNumber": 0
    }
}

Without first reading the "propertytypes.json", which appears to be a editor-specific file, and not part of the TMX spec, how would one determine these value types? Is SomeString a string or a color? Is SomeNumber a float or an integer? This poses a problem when parsing, especially with statically-typed languages.

I am writing general-purpose parser, and encountered this issue when implementing the JSON format it. The XML-based format includes this information, and does not require reading "propertytypes.xml". Compare to the equivalent XML output:

<property name="Something" type="class" propertytype="MyClass">
    <properties>
        <property name="SomeString" value="#ff000000"/>
        <property name="SomeNumber" type="int" value="0"/>
    </properties>
</property>

There is no ambiguity here, nor need to require the user to provide additional files to load or class definitions in order to determine the custom class types. One can easily parse it out exactly as they would parse any other <properties> tag.

While the "propertytypes" outputs are pretty much self-documenting, they are not part of the format documentation, and seem like misc. project files related to Tiled, not something to be shipped with a game, etc. This make it somewhat unclear if loading them is indeed the idiomatic way to process the JSON format. Considering the XML format would not need them, and they use the more generic xml/json extensions, the documentation may benefit from some additional clarity regarding them.

To Reproduce Steps to reproduce the behavior:

  1. Create a custom class.
  2. Save it as JSON.

Expected behavior The value of the property contains full property definitions like its XML counterpart, and not abbreviated key/value pairs.

Specifications:

  • OS: Arch Linux (btw)
  • Tiled Version: 1.10.2

I am writing general-purpose parser, and encountered this issue when implementing the JSON format it. The XML-based format includes this information, and does not require reading "propertytypes.xml". Compare to the equivalent XML output:

A big difference between XML and JSON is that JSON already has all the basic value types, whereas XML has just strings. So, in XML the type attribute is absolutely necessary, whereas in JSON it is already clear whether the property is a number, string, bool or some object.

Granted, JSON does not account for custom types like color, file path and user-defined types. But I thought the need for this would be rare enough that the simpler JSON object form would be preferable, and those who do need more type information could read it from the project file (or from an exported types file). And indeed this types file format is lacking documentation.

Even when loading an XML file, the included type information might not be enough. Only explicitly set members are included for example, so without reading in the types from another source, an importer will not know the types or default values for unset members.

In general, my expectation is that most users would use the custom type system to tell Tiled about the options supported by their engine. Maybe you can elaborate a bit about why you need the additional type information? Is this just about providing a bit of additional convenience, by already parsing a color value? And what do you plan to do with nested custom classes?

posted by bjorn over 1 year ago

Even when loading an XML file, the included type information might not be enough. Only explicitly set members are included for example, so without reading in the types from another source, an importer will not know the types or default values for unset members.

While technically accurate in the strictest sense, there is documented and defined behavior for when a value is not included, so it has its own semantic meaning. For example, if an XML property is supplied that does not include the "type" attribute, the specification states that it is a string type. The issue I bring up in this case is that there is no defined behavior, rule to follow, or assumption that can be made with custom user-defined types in JSON format.

...whereas in JSON it is already clear whether the property is a number, string, bool or some object.

This is crux of the issue that I want to lightly push back on, as it isn't always clear. I completely agree that you get some information, you know it is a number, but not what type of number.

{ "Value": 0 }

Now for me, not as the end-user who understands the structure of their own custom classes that can safely make assumptions, but as someone developing a library for such a person to parse it: how do I know what type of number that is? Without even concern if it is an object ID or an integer, is that even an int or is it a float? Floats without significant digits following the decimal place truncate it, so it isn't even possible to use the format of the number to determine what it is.

As it stands, when loading JSON, there is two options:

  • Require the user to first supply the properties file to load so that custom types are known
  • Assume that every string is a string, not a file or color (which every other part of library serializes to a color structure and doesn't keep it as a string) and assume that every generic JSON number is a double, regardless if it is as an integer or object ID.

One possible solution to consider that doesn't require adding information to the properties is supplying the relative path to the file where they are defined in, perhaps as field in the parent property. This would make it less cumbersome for end-users, maintaining the "just load the file" behavior without any extra gymnastics of pre-loading files., while keeping the API surface the same, just one optional field to the parent property, something like:

{
       "name": "Clever Name",
       "source": "../propertytypes.json",
       "propertytype": "CustomClass",
       "type": "class",
       "value": {
              "StringOrFileOrColor": "#deadbeef",
              "FloatOrIntegerOrObject": 5
       }
}

Just an idea, not sure how feasible it is.

I don't want to give the wrong impression, this is far from being a critical issue, just a minor annoyance for implementors of the specification, not a common issue that dramatically effects end-users. Users can load a map without having to first manually load the tilesets/templates it uses, so it feels unintuitve to enforce this behavior on them for one type of object, while using one specific file format, but not others.

posted by ForeverZer0 over 1 year ago

Who is the "user" in your scenario? Are you writing a generic parser for multiple engines?

Generally, the use scenario, as bjorn described, is that the custom types represent things that are already known to the engine or game, so whether a given property should be int or float doesn't need to be specified, the engine will interpret the value as whatever it expects for that specific property.

That said, even in my engine-specific TMX parser, I do rely on the type of a property to decide how to parse it (in that case, whether a "script" property is a string, or a file path), and though in my case come from an object class rather than a custom property type, the same distinction could be useful in a custom property type as well, so a clearer type indication would be nice.

Or, coming at the issue another way: it would be easier to parse custom property types if they behave exactly like containers of properties the same way objects, etc do, and didn't have a whole different syntax to them. That is, it would be nice if the value of a class was an array of properties the same way the properties of an object are. JSON changed in 1.2 to store custom properties from a dictionary to an array, why did custom types go back to a dictionary at all?

posted by eishiya over 1 year ago

@eishiya "User" is a consumer of a general-purpose library that can parse the TMX format into idiomatic types for the language, one that could be used as pre-built component in a game engine, not specifically for a personal project that needed to load some maps.

Like any parsing library for any format, things get converted to structures idiomatic and user-friendly to that language. Using this library with a specific example: A "color" doesn't keep getting passed around as string, there is a Color struct. Anywhere in a document a color is defined, it gets parsed out as a Color. This breaks down with this particular scenario, as it can't be assumed to be a color. The inconsistency in the format spreads, and now there will be an inconsistency where a color is not a Color, but is now a string that needs converted.

While not a huge problem, it seems unnecessary and completely avoidable, as all the required information already exists, it is simply omitted from any way to handle it automatically. Ideally when they went to reference that property, it would already be a Color, not a string that needed parsed.

posted by ForeverZer0 over 1 year ago

Fund this Issue

$0.00
Funded
Only logged in users can fund an issue

Pull requests