skip to Main Content

I have JSON (for Ignition SCADA) that defines "tags" in the system which essentially defines a tree structure where the end tree nodes are the tags themselves with the configuration that defines them, and the sub-tree elements being folders. Each folder has a key called "tags" which holds a list of any tags or folders that are contained within them.

The most important parts of the JSON that defines a tag is its path which is made up of each "name" field in the JSON along the Folder path it lies in, similar to a filepath of a file. Elements of the path are separated by "/" e.g. for TagA below, its tag path would be "StackOverflow/FolderA/TagA".

Below is the tree in the Ignition designer:

Tags Displayed in Ignition Tag Browser GUI

Below is the JSON that represents these tags:

{
  "name": "StackOverflow",
  "tagType": "Folder",
  "tags": [
    {
      "name": "FolderA",
      "tagType": "Folder",
      "tags": [
        {
          "valueSource": "memory",
          "dataType": "Int8",
          "name": "TagA",
          "tooltip": "This is tag a",
          "value": 15,
          "tagType": "AtomicTag",
          "engUnit": "%"
        },
        {
          "valueSource": "memory",
          "dataType": "Int8",
          "name": "TagB",
          "tooltip": "This is tag b",
          "value": 98,
          "tagType": "AtomicTag",
          "engUnit": "%"
        }
      ]
    }
  ]
}

If I make changes to these tags/folders by:

  • adding a new tag, StackOverflow/FolderA/TagC
  • change value of StackOverflow/FolderA/TagA.tooltip
  • change value of StackOverflow/FolderA/TagA.value
  • delete StackOverflow/FolderA/TagB

I want to get a report of this as a list of changes that I can export into Excel, e.g. something like:

[{'tagpath': 'StackOverflow/FolderA/TagC', 'changedFrom': None, 'changedTo': 'added'},
{'tagpath': 'StackOverflow/FolderA/TagA.tooltip', 'changedFrom': 'This is tag a', 'changedTo': 'This is tag a with changes'},
{'tagpath': 'StackOverflow/FolderA/TagA.value', 'changedFrom': 15, 'changedTo': 59},
{'tagpath': 'StackOverflow/FolderA/TagB', 'changedFrom': None, 'changedTo': 'deleted'}
]

This is the changes JSON:

{
  "name": "StackOverflow",
  "tagType": "Folder",
  "tags": [
    {
      "name": "FolderA",
      "tagType": "Folder",
      "tags": [
        {
          "valueSource": "memory",
          "dataType": "Int8",
          "name": "TagC",
          "tooltip": "This is tag a with changes",
          "value": 59,
          "tagType": "AtomicTag",
          "engUnit": "%"
        },
        {
          "valueSource": "memory",
          "dataType": "Int8",
          "name": "TagA",
          "tooltip": "This is tag a with changes",
          "value": 59,
          "tagType": "AtomicTag",
          "engUnit": "%"
        }
      ]
    }
  ]
}

enter image description here

What I’m not sure of is how best to compare these two files using Python. I can pull something together using simple for loops and comparing the json manually and keeping track of differences, but I was wondering if there is a better more sophisticated way?

2

Answers


  1. May be some packages will be useful. If you don’t want to use it, you can use iterator to instead for one by one.

    from jsondiff import diff
    json1 = {
        "name": "Bob",
        "age": 10,
        "sex": "male"
    }
    json2 = {
        "name": "Alice",
        "age": 10,
        "sex": "female"
    }
    
    difference1 = diff(json1, json2)
    difference2 = diff(json2, json1)
    print(difference1)
    print(difference2)
    

    result:
    {‘name’: ‘Alice’, ‘sex’: ‘female’}
    {‘name’: ‘Bob’, ‘sex’: ‘male’}

    I think you can use this to construct your change log.

    Login or Signup to reply.
  2. The problem can be solved with littletree (I’m the author).

    Assuming the original and modified data are stored as a nested dict in original and modified respectively

    from littletree import Node
    
    original_tree = Node.from_dict(original, identifier_name="name", children_name="tags")
    modified_tree = Node.from_dict(modified, identifier_name="name", children_name="tags")
    
    # Collect changes in a list
    changes = []
    for diff_node in original_tree.compare(modified_tree).iter_tree():
        diff_data = diff_node.data
        if not diff_data:
            continue  # Data was the same
        
        if "self" not in diff_data:
            changes.append({"tagpath": str(diff_node.path), "from": None, "to": "added"})
        elif "other" not in diff_data:
            changes.append({"tagpath": str(diff_node.path), "from": None, "to": "removed"})
        else:
            original_data, modified_data = diff_data["self"], diff_data["other"]
            for key, original_value in original_data.items():
                modified_value = modified_data[key]
                if original_value != modified_value:
                    changes.append({"tagpath": f"{diff_node.path}.{key}",
                                    "from": original_value,
                                    "to": modified_value})
    
    for change in changes:
        print(change)
    

    The result looks like this:

    {'tagpath': '/StackOverflow/FolderA/TagA.tooltip', 'from': 'This is tag a', 'to': 'This is tag a but changed'}
    {'tagpath': '/StackOverflow/FolderA/TagA.value', 'from': 15, 'to': 16}
    {'tagpath': '/StackOverflow/FolderA/TagB', 'from': None, 'to': 'removed'}
    {'tagpath': '/StackOverflow/FolderA/TagC', 'from': None, 'to': 'added'}
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search