Wouldn't JSONPatch, JSONPointer be better in Foundation?

Upon looking for third party library for the IETF standards for JSONPatch and JSONPointer I stumbled across DocC having those capabilities.

Sources/SwiftDocC/Model/Rendering/Variants/

  • JSONPatchApplier.swift
  • JSONPatchOperation.swift
  • JSONPointer.swift
  • PatchOperation.swift

Wouldn't JSONPatch, JSONPointer be better in Foundation with JSONEncode and JSONDecode ?

1 Like

In some sense, perhaps. Although on the other hand, I'd never heard of these before (despite having used JSON in many contexts for many years), so maybe not.

Foundation benefits from being relatively svelte - it's generally assumed to be fully present in Swift development even though Swift spans a huge range of platforms and hardware capabilities. So its contents need to be broadly useful and used.

It seems a shame to hide them in DocC, that's for sure. They would likely benefit both themselves and the community to be in their own package.

Maybe a better way to frame the question is "how can this API's be made public?"
Which will lead to where they can go.

I understand Foundation is supposed to be svelte vs. bloated.

With your years of JSON experiences, now that you know of the IETF RFC's and their capabilities, can you see in hindsight how they could have been beneficial in past projects?

It is good timing that the Foundation workgroup formed.

well… not quite. on platforms like iOS you get Foundation “for free” because it is a shared library on a platform with many other applications that use it. on the server, Foundation is not free, it is an enormous source of bloat.

it is quite frustrating how many packages (swift-markdown, async-http-client, etc.) have not made an effort to factor out Foundation-dependent functionality into overlay modules, it means we need to spend time offloading tasks that depend on those libraries to run on machines besides the server.

Hey Swift community!

Perhaps not within Foundation, but rather in a separate "JSONKit".

I prefer using JSONPath over JSONPatch+JSONPointer for JSON manipulation, and it seems that JSONPointer v2 isn't making much progress. Therefore, I'd advocate for JSONPath.

JSONPath offers a simple yet expressive syntax for querying and manipulating JSON data. It's widely used in other programming languages, and I believe it could be a valuable addition to Swift.

I've been using JSONPath in my Swift projects through the Sextant library. It has enabled me to efficiently perform various JSON operations, such as adding, removing, modifying JSON nodes, and changing node paths. This has been incredibly helpful.

I'm aligned with Craig Munro and believe that more powerful JSON manipulation tools should be available even if JSONPath or JSONPatch+JSONPointer are not used.

Like JSONPatch and JSONPointer, JSONPath is not a random tool; it has its own rfc in draft. By incorporating it into Swift, we align with established standards, ensuring compatibility and reliability.

JSONPath is just one piece of the puzzle. We can expand our JSON toolkit further by including others tools like JSONSchema verification and additional JSON manipulation tools. This comprehensive toolkit will simplify the creation, transformation, and validation of JSON format files in Swift.

I suggest that we consider creating a dedicated library, let's call it "JSONKit," to house these essential JSON tools. This library would become the go-to resource for Swift developers working with JSON data.

JSONKit Features:

  1. JSONPath Support: As the cornerstone of this toolkit, JSONPath will enable precise JSON querying and manipulation.

  2. JSONSchema Validation: We can also add JSONSchema validation, specifications, to ensure that our JSON documents meet specified criteria.

  3. JSON Partial Decoding tool that can verify that a string or data is valid JSON and allow for partial decoding of a file while injecting new data into it.

  4. Providing a great Developer experience by using ResultBuilder to write JSONTransformers.

Additionally, it might be worth discussing this with the server-side Swift community. Having these tools available on the server side could be a significant asset for Swift developers working on backend projects.

Cheers,

Jeff

I'm already looking into JSONPath and JSONPatch support in my library, IkigaJSON. While I do want to support JSONSchema, I think that's best for a separate library, especially because it can get quite complex. I have some existing source code to work with for all of these three use cases, albeit unfinished.

I'm curious what your ideas are about using result builders. I don't see any particular use case there, but happy to hear some creative use cases that I have not yet considered.

1 Like

RFC 6901 is for JSON Pointers, there is only a draft of JSON Path as it is working it's way through the standardization process for the past few years already. Drafts are not Standards, they can die and wither, or take an inordinate amount of time to make progress.

Can you double check the RFC 8951 reference?

The only JSON Schema docs I am finding are expired drafts.

You are right; I mixed up my links (I will correct my previous post).

JSONSchema seems to be maintained here, but the RFC seems to be abandoned.

And yes, JSONPath is in draft and appears to be active.

I believe that result builders can greatly enhance simplicity, convenience, correctness and the overall developer experience, even though my ideas on this topic are still evolving.

One concept I've been thinking of is having JSONPath working like Regex, enabling developers to craft intricate queries more naturally. Additionally, the ability to validate JSONPath at compile-time would be great.

A good use case could involve employing ResultBuilders for creating JSON Transformers. These transformers, which are essentially functions capable of reshaping JSON data according to predefined rules, could streamline the process significantly. Instead of performing the Codable dance of decoding, processing, and encoding, we could achieve this in a single pass.

In my current project, I find myself frequently in need of performing various JSON transformations.

Currently, accomplishing such tasks involves the Codable paradigm, which can be somewhat limiting. For example, when dealing with numerous JSON variations, I'd rather not create 150 different JSON models. Instead, I prefer to work in a more abstract manner, where I understand that some part of the data structure will remain consistent sometimes. While Swift Macros can alleviate some of these limitations, I firmly believe that directly handling JSON during the parsing phase can lead to more efficient and intuitive workflows.

Ultimately, I'm excited about the potential of result builders in enhancing JSON manipulation.

This is a exemple of my transformer config file

These are some of the rules that I have that are perfomed of a lot of various json files.

[
    {
      "action": "replace-key",
      "parent_node": "$.vp[?(@.web)]",
      "from": "web",
      "to": "apple"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*.color.border[?(@.active)]",
      "from": "active",
      "to": "focus"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*.color.content[?(@.default)]",
      "from": "default",
      "to": "text"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*.color[?(@['primary-label'])]",
      "from": "primary-label",
      "to": "text"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*.color[?(@['question-icon'])]",
      "from": "question-icon",
      "to": "icon"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..button.color..*[?(@.default)]",
      "from": "default",
      "to": "enabled"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..button.color..*[?(@.active)]",
      "from": "active",
      "to": "pressed"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..link.color..*[?(@.default)]",
      "from": "default",
      "to": "enabled"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..link.color..*[?(@.active)]",
      "from": "active",
      "to": "pressed"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..radio.color..*[?(@.default)]",
      "from": "default",
      "to": "enabled"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..radio.color..*[?(@.active)]",
      "from": "active",
      "to": "pressed"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..checkbox.color..*[?(@.default)]",
      "from": "default",
      "to": "enabled"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..checkbox.color..*[?(@.active)]",
      "from": "active",
      "to": "pressed"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..['icon-button'].color..*[?(@.default)]",
      "from": "default",
      "to": "enabled"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..['icon-button'].color..*[?(@.active)]",
      "from": "active",
      "to": "pressed"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..color..*[?(@.container)]",
      "from": "container",
      "to": "background"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..color..*[?(@.content)]",
      "from": "content",
      "to": "foreground"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['border-radius'])]..*[?(@['bottom-left'])]",
      "from": "bottom-left",
      "to": "bottom-leading"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['border-radius'])]..*[?(@['bottom-right'])]",
      "from": "bottom-right",
      "to": "bottom-trailing"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['border-radius'])]..*[?(@['top-left'])]",
      "from": "top-left",
      "to": "top-leading"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['border-radius'])]..*[?(@['top-right'])]",
      "from": "top-right",
      "to": "top-trailing"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['padding'])]..*[?(@['block-end'])]",
      "from": "block-end",
      "to": "bottom"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['padding'])]..*[?(@['inline-start'])]",
      "from": "inline-start",
      "to": "leading"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['padding'])]..*[?(@['block-start'])]",
      "from": "block-start",
      "to": "top"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['padding'])]..*[?(@['inline-end'])]",
      "from": "inline-end",
      "to": "trailing"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['padding'])]..*[?(@['bottom'])]",
      "from": "bottom",
      "to": "bottom"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['padding'])]..*[?(@['end'])]",
      "from": "end",
      "to": "trailing"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['padding'])]..*[?(@['start'])]",
      "from": "start",
      "to": "leading"
    },
    {
      "action": "replace-key",
      "parent_node": "$.vp..*[?(@['padding'])]..*[?(@['top'])]",
      "from": "top",
      "to": "top"
    },
    {
      "action": "add-node",
      "parent_node": "$.vp..component.badge",
      "key": "border-width",
      "value": {
        "value": "{vp.semantic.border-width.none}",
        "type": "borderWidth"
      }
    },
    {
      "action": "add-node",
      "parent_node": "$.vp..component.badge.color",
      "key": "border",
      "value": {
        "value": "{vp.semantic.color.border.brand}",
        "type": "color"
      }
    },
    {
      "action": "add-object-duplicate-child-in-each",
      "parent_node": "$.vp..component.text-input.sizing",
      "objects": [
        "small",
        "medium",
        "large"
      ]
    },
    {
      "action": "add-object-duplicate-child-in-each",
      "parent_node": "$.vp..subcomponent.form.error.sizing",
      "objects": [
        "small",
        "medium",
        "large"
      ]
    },
    {
      "action": "add-object-duplicate-child-in-each",
      "parent_node": "$.vp..subcomponent.form.error.sizing",
      "objects": [
        "small",
        "medium",
        "large"
      ]
    },
    {
      "action": "add-missing-padding",
      "parent_node": "$..padding",
      "forms": [
        ["bottom", "end", "start", "top"],
        ["block-end", "inline-start", "block-start", "inline-end"],
      ],
      "value": {
        "value" : "{vp.core.spacing.0}",
        "type" : "spacing",
      }
    },
    {
      "action": "replace",
      "path": "$..subcomponent.form.error.spacing.icon",
      "value": {
        "alignement" : {
          "value" : ".firstTextBaseline",
          "type" : "other",
        }
      }
    },
    {
      "action": "remove",
      "path": "$.node.to.remove"
    }
  ]