@Override
    public ObjectNode encode(FlowEntry flowEntry, CodecContext context) {
        checkNotNull(flowEntry, "Flow entry cannot be null");

        ObjectNode result = context.mapper().createObjectNode()
                .put(GROUP_ID, flowEntry.groupId().id())
                .put(STATE, flowEntry.state().toString())
                //FIXME life is destroying precision (seconds granularity is default)
                .put(LIFE, flowEntry.life())
                .put(LIVE_TYPE, flowEntry.liveType().toString())
                .put(LAST_SEEN, flowEntry.lastSeen())
                .put(PACKETS, flowEntry.packets())
                .put(BYTES, flowEntry.bytes())
                // encode FlowRule-specific fields using the FlowRule codec
                .setAll(context.codec(FlowRule.class).encode((FlowRule) flowEntry, context));

        if (flowEntry.treatment() != null) {
            final JsonCodec<TrafficTreatment> treatmentCodec =
                    context.codec(TrafficTreatment.class);
            result.set(TREATMENT, treatmentCodec.encode(flowEntry.treatment(), context));
        }

        if (flowEntry.selector() != null) {
            final JsonCodec<TrafficSelector> selectorCodec =
                    context.codec(TrafficSelector.class);
            result.set(SELECTOR, selectorCodec.encode(flowEntry.selector(), context));
        }

        return result;
    }
@Test
    public void testEncode() throws IOException {
        InputStream jsonStream = FlowEntryCodec.class.getResourceAsStream(JSON_FILE);
        ObjectNode jsonString = (ObjectNode) context.mapper().readTree(jsonStream);

        ObjectNode expected = flowEntryCodec.encode(FLOW_ENTRY, context);
        // only set by the internal FlowRule encoder, so should not appear in the JSON string
        expected.remove("id");
        expected.remove("appId");
        expected.remove("tableName");

        // only set by the FlowEntry encoder but not used for the decoder
        // so should not appear in the JSON, or a decoding error occurs
        expected.remove(FlowEntryCodec.GROUP_ID);
        expected.remove(FlowEntryCodec.LAST_SEEN);

        // assert equality of those values separately. see below
        assertEquals(expected.get(FlowEntryCodec.LIFE).asLong(), jsonString.get(FlowEntryCodec.LIFE).asLong());
        assertEquals(expected.get(FlowEntryCodec.PACKETS).asLong(), jsonString.get(FlowEntryCodec.PACKETS).asLong());
        assertEquals(expected.get(FlowEntryCodec.BYTES).asLong(), jsonString.get(FlowEntryCodec.BYTES).asLong());

        // if those numeric values are included in expected as a result of the encoding,
        // AssertionError occurs even though both expected and jsonString are semantically identical
        expected.remove(FlowEntryCodec.LIFE);
        expected.remove(FlowEntryCodec.PACKETS);
        expected.remove(FlowEntryCodec.BYTES);
        jsonString.remove(FlowEntryCodec.LIFE);
        jsonString.remove(FlowEntryCodec.PACKETS);
        jsonString.remove(FlowEntryCodec.BYTES);

        assertEquals(expected, jsonString);
    }