Let's say I have a simple JSON like:
"values": {
"A": 0,
"B": 0,
"C": 0,
"D": 0,
"E": 0,
}
A, B, C are part of the basic schema T1 while D,E are part of an extending schema T2. Therefore the schema T1 has 3 properties while T2 has 5 properties. I would like to avoid write A,B,C twice, in both schemas.
Is there anyway in JSON Schema to extend the list of properties?
If you want to split out your schemas in a way which reflects extending objects, where some of your schemas have additionalProperties: false, you need to hoist up the additionalProperties: false.
You can use a definition in each schema to define the object without additionalProperties: false, then the root schema allOf reference the definition with additionalProperties: true. Then in the other schemas, you can reference the previous schema's definition.
Unfortunately, it's a limitation with draft-7. In draft-8, it is possible to do what you want with a new keyword, but it's not published yet.
Related
at the moment I have a very simple array:
val name = arrayListOf(
"Steve",
"David",
"Chris,
"Bev")
MyResult.text = name[2]
Giving “Chris”.
But I would like to use the same data but including the age with something like:
val name = array????(
0, "Steve", 20,
1, "David", 28,
2, "Chris, 28,
3, "Bev", 46)
MyResult.text = name[2,0]
Giving the same result, ie using something that allows me to specify the position in the array please to be able to return the item?
Any help appreciated!
Arrays (and lists, which are more generally useful) are for when every element is the same type.
In this case, that's not true. You have a mix of types, because you have internal structure: some elements relate directly to others, in a regular way. What you have is effectively a list of people, each with several fields. So code that! It's very straightforward:
data class Person(val id: Int, val name: String, val age: Int)
val people = listOf(Person(0, "Steve", 20),
Person(1, "David", 28),
Person(2, "Chris", 28),
Person(3, "Bev", 46))
This way, the whole type system is working for you. Because you've told the compiler what those fields represent and how they relate to each other, it can prevent you from mixing them up by mistake, and it knows what you can do with each one. You can then manipulate them in all sorts of ways. Not just getting the person at given position in the list:
val firstPerson = people[0]
But getting a person by name:
val david = people.find{ it.name == "David" }
Or finding the youngest person:
val youngest = people.minByOrNull{ it.age }
Or getting their names in alphabetical order:
val names = people.map{ it.name }.sorted()
Or many other things. The Kotlin standard library has a lot of extension functions and other machinery for handling lists, and Kotlin's data classes make it very easy to create value objects like this.
If there's no inherent ordering between the people, then maybe a set would be a better fit.
Or if you often refer to them by ID, then you could store them in a map with their ID as the key.
It all depends what you're going to do with them.
But an array is unlikely to be the best option. Arrays are a necessary evil, needed for a few specific things (varargs, implementing higher-level structures, storing primitive values efficiently, and interoperating with legacy code), but for most other uses lists are more powerful, more flexible, and better supported. Most Kotlin code should use lists instead of arrays.
I'm new to ReasonML, but I read through most of the official documents. I could go through the casual trial and errors for this, but since I need to write codes in ReasonML right now, I'd like to know the best practices of iterating keys and values of reason record types.
I fully agree with #Shawn that you should use a more appropriate data structure. A list of tuples, for example, is a nice and easy way to pass in a user-defined set of homogeneous key/value pairs:
fooOnThis([
("test1", ["a", "b", "c"]),
("test2", ["c"]),
])
If you need heterogeneous data I would suggest using a variant to specify the data type:
type data =
| String(string)
| KvPairs(list((string, data)));
fooOnThis([
("test1", [String("a"), String("b"), String("c")]),
("test2", [String("c"), KvPairs([("innerTest", "d")])]),
])
Alternatively you can use objects instead of records, which seems like what you actually want.
For the record, a record requires a pre-defined record type:
type record = {
foo: int,
bar: string,
};
and this is how you construct them:
let value = {
foo: 42,
bar: "baz",
};
Objects on the other hand are structurally typed, meaning they don't require a pre-defined type, and you construct them slightly differently:
let value
: {. "foo": int, "bar": string }
= {"foo": 42, "bar": "baz"};
Notice that the keys are strings.
With objects you can use Js.Obj.keys to get the keys:
let keys = Js.Obj.keys(value); // returns [|"foo", "bar"|]
The problem now is getting the values. There is no Js.Obj API for getting the values or entries because it would either be unsound or very impractical. To demonstrate that, let's try making it ourselves.
We can easily write our own binding to Object.entries:
[#bs.val] external entries: Js.t({..}) => array((string, _)) = "Object.entries";
entries here is a function that takes any object and returns an array of tuples with string keys and values of a type that will be inferred based on how we use them. This is neither safe, because we don't know what the actual value types are, or particularly practical as it will be homogeneously typed. For example:
let fields = entries({"foo": 42, "bar": "baz"});
// This will infer the value's type as an `int`
switch (fields) {
| [|("foo", value), _|] => value + 2
| _ => 0
};
// This will infer the value's type as an `string`, and yield a type error
// because `fields` can't be typed to hold both `int`s and `string`s
switch (fields) {
| [|("foo", value), _|] => value ++ "2"
| _ => ""
};
You can use either of these switch expressions (with unexpected results and possible crashes at runtime), but not both together as there is no unboxed string | int type to be inferred in Reason.
To get around this we can make the value an abstract type and use Js.Types.classify to safely get the actual underlying data type, akin to using typeof in JavaScript:
type value;
[#bs.val] external entries: Js.t({..}) => array((string, value)) = "Object.entries";
let fields = entries({"foo": 42, "bar": "baz"});
switch (fields) {
| [|("foo", value), _|] =>
switch (Js.Types.classify(value)) {
| JSString(str) => str
| JSNumber(number) => Js.Float.toString(number)
| _ => "unknown"
}
| _ => "unknown"
};
This is completely safe but, as you can see, not very practical.
Finally, we can actually modify this slightly to use it safely with records as well, by relying on the fact that records are represented internally as JavaScript objects. All we need to do is not restrict entries to objects:
[#bs.val] external entries: 'a => array((string, value)) = "Object.entries";
let fields = keys({foo: 42, bar: 24}); // returns [|("foo", 42), ("bar", 24)|]
This is still safe because all values are objects in JavaScript and we don't make any assumptions about the type of the values. If we try to use this with a primitive type we'll just get an empty array, and if we try to use it with an array we'll get the indexes as keys.
But because records need to be pre-defined this isn't going to be very useful. So all this said, I still suggest going with the list of tuples.
Note: This uses ReasonML syntax since that's what you asked for, but refers to the ReScript documentation, which uses the slightly different ReScript syntax, since the BuckleScript documentation has been taken down (Yeah it's a mess right now, I know. Hopefully it'll improve eventually.)
Maybe I am not understanding the question or the use case. But as far as I know there is no way to iterate over key/value pairs of a record. You may want to use a different data model:
hash table https://caml.inria.fr/pub/docs/manual-ocaml/libref/Hashtbl.html
Js.Dict (if you're working in bucklescript/ReScript) https://rescript-lang.org/docs/manual/latest/api/js/dict
a list of tuples
With a record all keys and value types are known so you can just write code to handle each one, no iteration needed.
I need a system to describe input and output data types.
A type can be a primitive type like "integer" or "string' or a custom type like "TensorFlow model" or "CSV table".
The validation properties I'm adding to the data validation properties that has big resemblance to the JsonSchema validation properties.
It might be nice to describe the input and output data types using the JsonSchema language.
What's the best way to do that?
I had something like this in mind:
{"inputs": {
"model": {"type": "binary", "format": "TensorFlow model", "required": "true"},
"rounds": {"type": "integer", "minimum": 1, "default": 100}
}}
P.S. I find the way type and format are used really confusing. Types are basic and general while formats are specific. My associations are the opposite. Usually you have many specialized types that can be expressed in one of the few formats.
The primary aim of JSON Schema is to provide the format of JSON data.
The validation specification (draft-7) documents format in part as follows:
Implementations MAY add custom format attributes. Save for agreement
between parties, schema authors SHALL NOT expect a peer
implementation to support this keyword and/or custom format
attributes.
https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-7.1
This means, you can add any format you want, but you can't expect it to work elsewhere. You should form agreements (or document what you mean) with anyone else that you expect to be able to use your schemas to validate the data you're providing.
I have this scenario at hand:
I have an API server holding enum value and sending it using Restfull API.
The client will receive it and needs to act according to the value.
My main question: Which is better - sending the int value or the string? I can see benefits for both approaches.
Is there any way to avoid holding the enum in both sides? I am not familiar with one that actually can be useful.
Thanks!
If the API server maintains the enum, the client could fetch it by:
GET /enums
... which will return a list of enum values:
[
{ "id" : "1001", "value" : "enum-item-1" },
{ "id" : "1002", "value" : "enum-item-2" },
...
{ "id" : "100N", "value" : "enum-item-3" },
]
Thus allowing the client to fetch one of the enum items:
GET /enums/1017
Or perhaps perform an operation on it:
POST /enums/1017/disable
Normally, one would only refer to the enum items by their unique ID - and if the client always starts by querying the server for the enum list, you will not need to maintain the enum on the client and server.
But - if the values in your business case are permanently unique and there is a compelling reason to have 'nicer' human-readable URLs, one could use:
GET /enums/enum-item-26
Generally, this is not best practice, as the enum item value probably has business meaning and thus might change. Even though that currently seems unlikely.
For the following json string :
{
"abc" : 123,
"def" : 345
}
The following schema considers it valid :
{
"$schema": "http://json-schema.org/draft-03/schema#",
"title": "My Schema",
"description": "Blah",
"type": "object",
"patternProperties": {
".+": {
"type": "number"
}
}
}
However, changing the the patternProperties to properties still considers it valid. What then, is the difference between these 2 tags?
For the schema above all properties should be number. This data is invalid:
{ a: 'a' }
If you replace patternProperties with properties only property '.+' should be number. All other properties can be anything. This would be invalid:
{ '.+': 'a' }
This would be valid:
{ a: 'a' }
The properties (key-value pairs) on an object are defined using the properties keyword. The value of properties is an object, where each key is the name of a property and each value is a JSON schema used to validate that property.
additionalProperties can restrict the object so that it either has no additional properties that weren’t explicitly listed, or it can specify a schema for any additional properties on the object. Sometimes that isn’t enough, and you may want to restrict the names of the extra properties, or you may want to say that, given a particular kind of name, the value should match a particular schema. That’s where patternProperties comes in: it is a new keyword that maps from regular expressions to schemas. If an additional property matches a given regular expression, it must also validate against the corresponding schema.
Note: When defining the regular expressions, it’s important to note that the expression may match anywhere within the property name. For example, the regular expression "p" will match any property name with a p in it, such as "apple", not just a property whose name is simply "p". It’s therefore usually less confusing to surround the regular expression in ^...$, for example, "^p$".
for further reference --http://spacetelescope.github.io/understanding-json-schema/reference/object.html
Semantic of properties:
If you declare a property with a key included in properties, it must satisfy the schema declared in properties.
Semantic of patternProperties:
If you declare a property and the key satisfy the regex defined in patternProperties, it must satisfy the schema declared in patternProperties.
According to the docs, properties priority is higher than patternProperties, meaning that the schema is validated against patternProperties only if there has not been a match in properties first.
A JSON object is composed of key: value pairs. In a schema the key correspond to a property and for the value part we define it's data type and some other constratints.
Therefore the following schema
{
"type": "object",
"properties": {
"a": {
"type": "number"
}
}
will only validate a JSON object with the key "a" that is an object like {"a": 1}. An object like {"b": 1} won't validate
Meanwhile the patternProperties tag allows you to define properties using a regex. In this you basically don't need to define all the properties one after another. A use case of this will be for example if you don't know the name of the keys in advance but you know that all the keys match a certain pattern.
Hence your schema can validate {"a": 1} as well as {"b": 1}
The patternProperties tag does the job of an additionalProperties tag but in addition allows you to have a finer control on the keys