0

I’m working on splitting a SignalK JSON object into canonical JSON items representing each value.

The original JSON looks like this:

{
"mmsi": "urn:mrn:signalk:uuid:5377770-4ee4-4a4b-3230-888037332031",
"name": "Mona",
"navigation": {
    "position": {
        "timestamp": "1991-09-03T03:5:36.000Z",
        "latitude": 51.763691,
        "longitude": 9.501367,
        "altitude": 0.000000,
        "source": "N0183-01"
    },
    "courseOverGroundTrue": {
        "value": 23.000000
    },
    "speedOverGround": {
        "value": 2.010289
    }
},
"environment": {
    "depth": {
        "belowTransducer": {
            "value": 12.700000
        }
    },
    "wind": {
        "angleApparent": {
            "value": 0.174533
        },
        "speedApparent": {
            "value": 0.000000
        }
    }}}

The needed transformed JSON looks like this, with JSON elements representing each value, and with item naming representing the whole path of the value.

{
"items": [{
        "columns": {
            "assetId": "urn:mrn:signalk:uuid:5377770-4ee4-4a4b-3230-888037332031",
            "description": "EnvironmentWindSpeedApparent",
            "isStep": false,
            "name": "EnvironmentWindSpeedApparent",
            "timestamps": 1523962903470,
            "type": "numerical",
            "values": 0.0
        },
        "key": "20180417-130143470EnvironmentWindSpeedApparent5377770-4ee4-4a4b-3230-888037332031"
    }, {
        "columns": {
            "assetId": "urn:mrn:signalk:uuid:5377770-4ee4-4a4b-3230-888037332031",
            "description": "EnvironmentWindAngleApparent",
            "isStep": false,
            "name": "EnvironmentWindAngleApparent",
            "timestamps": 1523962903470,
            "type": "numerical",
            "values": 0.174533
        },
        "key": "20180417-130143470EnvironmentWindAngleApparent5377770-4ee4-4a4b-3230-888037332031"
    }, {
        "columns": {
            "assetId": "urn:mrn:signalk:uuid:5377770-4ee4-4a4b-3230-888037332031",
            "description": "EnvironmentDepthBelowTransducer",
            "isStep": false,
            "name": "EnvironmentDepthBelowTransducer",
            "timestamps": 1523962903470,
            "type": "numerical",
            "values": 12.7
        },
        "key": "20180417-130143470EnvironmentDepthBelowTransducer5377770-4ee4-4a4b-3230-888037332031"
    }, {
        "columns": {
            "assetId": "urn:mrn:signalk:uuid:5377770-4ee4-4a4b-3230-888037332031",
            "description": "NavigationPositionLongitude",
            "isStep": false,
            "name": "NavigationPositionLongitude",
            "timestamps": 1523962903470,
            "type": "numerical",
            "values": 9.501367
        },
        "key": "20180417-130143470NavigationPositionLongitude5377770-4ee4-4a4b-3230-888037332031"
    }, {
        "columns": {
            "assetId": "urn:mrn:signalk:uuid:5377770-4ee4-4a4b-3230-888037332031",
            "description": "NavigationPositionLatitude",
            "isStep": false,
            "name": "NavigationPositionLatitude",
            "timestamps": 1523962903470,
            "type": "numerical",
            "values": 51.763691
        },
        "key": "20180417-130143470NavigationPositionLatitude5377770-4ee4-4a4b-3230-888037332031"
    }, {
        "columns": {
            "assetId": "urn:mrn:signalk:uuid:5377770-4ee4-4a4b-3230-888037332031",
            "description": "NavigationCourseOverGroundTrue",
            "isStep": false,
            "name": "NavigationCourseOverGroundTrue",
            "timestamps": 1523962903470,
            "type": "numerical",
            "values": 23.0
        },
        "key": "20180417-130143470NavigationCourseOverGroundTrue5377770-4ee4-4a4b-3230-888037332031"
    }, {
        "columns": {
            "assetId": "urn:mrn:signalk:uuid:5377770-4ee4-4a4b-3230-888037332031",
            "description": "NavigationSpeedOverGround",
            "isStep": false,
            "name": "NavigationSpeedOverGround",
            "timestamps": 1523962903470,
            "type": "numerical",
            "values": 2.010289
        },
        "key": "20180417-130143470NavigationSpeedOverGround5377770-4ee4-4a4b-3230-888037332031"
    }
    ]}

How to do this transformation in a flexible way that adopts to changing sub-nodes being available in the original JSON ? I’m transforming it now in a simplistic way, but would like to know if it can be done using JsonReader , gson or other ways of iterating through the original JSON object.

1 Answer 1

0

I ended up defining the object structure representing the transformed json, and writing a custom serializer to do the transformation. It could be more efficient ways of doing it, but this approach seems to be working OK.

public class SignalKDeserializer implements JsonDeserializer<TargetObject> {
//written based on examples from http://www.javacreed.com/gson-deserialiser-example/

final TargetObject targetObject = new TargetObject ();

@Override
public TargetObject deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
        throws JsonParseException {
    final JsonObject jsonObject = json.getAsJsonObject();
    traverse (jsonObject,0,"", mmsi);
    return targetObject;

}

private void traverse (JsonObject jsonObject, Integer level, String parentName, String mmsi) {
    Set<String> keys = jsonObject.keySet();
    Iterator<?> keysIterator = keys.iterator ();
    while( keysIterator.hasNext ()) {
        String key = (String) keysIterator.next ();
        String signalName = parentName+upperCaseFirst (key); //setting signalName to complete path of value
        if (jsonObject.get (key) instanceof JsonObject) {
            traverse ((jsonObject.get (key)).getAsJsonObject (),level+1,upperCaseFirst (signalName),mmsi);
        } else if (jsonObject.get (key) instanceof JsonElement) {
            if (level>0) {
                try {
                    final Double value = jsonObject.get(key).getAsDouble ();
                    calendar = Calendar.getInstance ();
                    Long timeStamp = calendar.getTimeInMillis ();
                    Item targetItem = new Item ();
                    targetItem.columns.setIsStep (false);
                    targetItem.columns.setAssetId (mmsi);
                    targetItem.columns.setTimestamps (calendar.getTimeInMillis ());
                    targetItem.columns.setType ("numerical");
                    targetItem.columns.setDescription (signalName);
                    targetItem.columns.setValues (value);
                    targetItem.columns.setName (signalName);
                    targetItem.setKey (signaldateformat.format (timeStamp) + mmsi);
                    targetObject.items.add (targetItem);
                }
                catch (NumberFormatException n) {
                    // Expected, the value is non numerical and  will not be transformed
                }
            }
        }
    }
}}

I'm using the serializer from my main class like this:

final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(TargetObject.class, new SignalKDeserializer ());
final Gson gson = gsonBuilder.create();
TargetObject targetObject = gson.fromJson (jsonSignalK,TargetObject.class);
String jsonOutString = gson.toJson (targetObject);

jsonOutString contains the transformed json I needed.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.