2

I'm making an Android App to manage a Unix computer remotely. The application also allows you to see real-time PC statistics (CPU temperature, CPU load, etc.). On the pc there is a Python server that through a socket sends a JSON string as a byte array to the app which contains the PC statistics, like this:

[123, 45, 67, 90, ...]

This is the contents of the byte array (I decoded it with a website):

{
    "author": "Anton",
    "type": "server info",
    "date": "08/01/2021",
    "content": "",
    "opt": {
        "cpu": {
            "freq": [3866.041875, 1600.0, 3900.0],
            "usage": [0.0, 0.0, 1.0, 2.0, 1.0, 1.0, 0.0, 2.0],
            "load": [0.5, 0.63, 0.61]
        },
        "memory": {
            "ram": [15.54, 12.01, 22.7, 2.85, 9.67]
        },
        "disks": {
            "ssd": [219.1, 13.0, 194.9, 6.3],
            "hdd": [0.0, 0.0, 0, 100.0]
        },
        "temps": {
            "core-0": [32.0, 85.0, 105.0],
            "core-1": [36.0, 85.0, 105.0],
            "core-2": [37.0, 85.0, 105.0],
            "core-3": [38.0, 85.0, 105.0]
        }
    },
    "attached": null
}

How can I transform the byte array into a structure like a HashMap or a JSON string, so that I can retrieve the PC statistics, and then view them in the App through ProgressiveBars?

11
  • 1
    Are you asking how to decode the byte array into a JSON string like you've already provided? Or are you asking about converting the JSON string you provided (decoded from the website) into a Java structure like a HashMap? Commented Jan 8, 2021 at 15:44
  • I should convert the byte array into a HashMap, or in any case a structure that allows me to retrieve every single data of it. Commented Jan 8, 2021 at 16:11
  • @DanieleMonaldi "JSON string as a byte array" is not clear. Does [123, 45, 67, 90, ...] represent the data logically (i.e., the first byte is value of 123, and so on), or does it represent the data physically (i.e., literally [, 1, 2, 3, ,, ... go over your network)? Commented Jan 8, 2021 at 22:24
  • @fluffy the server sends me the JSON string like the one I published in the form of a byte array, so for example 123 represents the character "{" Commented Jan 9, 2021 at 8:59
  • @DanieleMonaldi I have an idea of decoding it using Gson, but let me ask you three questions first. 1) 123, 45, 67, and 90 represent ASCII/UTF-8 characters {, -, C, and Z respectively (malformed but unusual JSON): they differ structurally from the JSON, but why? 2) When I was pretty-formatting your JSON, I found it was illegal: is that something your server sends to you or you just had some typos while posting it? 3) Shouldn't your server send a ready-to-use JSON rather than send it as an byte-array-encoded JSON that is not necessarily JSON-compatible? Commented Jan 9, 2021 at 10:15

3 Answers 3

3

You should be able to use the ObjectMapper from Jackson or any other JSON parser/serializer to achieve this. One option, using ObjectMapper is to just serialize the string directly into a hashmap like this:

byte src[] = getBytesFromServer();
ObjectMapper om = new ObjectMapper();
TypeReference<Map<String,Object>> tr = new TypeReference<Map<String,Object>> {};
Map<String,Object> val = om.readValue(src,tr);

There are a few variants of this that should be helpful to you. You can also achieve the same with other JSON libraries like GSon

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

1 Comment

A small edit on the 3rd line to correct the syntax - TypeReference<Map<String,Object>> tr = new TypeReference<Map<String,Object>> () {};
0

In the meantime, while your server code is not yet fixed, I can provide an example of adapting the byte-encoded response to what you can parse, for the "educational" purposes to avoid bad practices in the future (simply don't do the following).

The idea to solve your problem is adapting the incoming payload (byte-array-encoded text) to a byte/character-stream that can be consumed as JSON.

Using the org.json is might be implemented like this (however, I'm not sure if I've implemented it properly):

public final class Decoding {

    private Decoding() {
    }

    public static Reader of(final Reader reader) {
        final JSONTokener jsonTokener = new JSONTokener(reader);
        return new Reader() {
            private boolean beginToProcess = true;
            private boolean endToProcess = true;

            @Override
            public int read(@Nonnull final char[] buffer, final int offset, final int length)
                    throws IOException {
                if ( beginToProcess ) {
                    final char ch = jsonTokener.nextClean();
                    if ( ch != '[' ) {
                        throw new IOException("Not an array begin: " + ch);
                    }
                    beginToProcess = false;
                }
                if ( !jsonTokener.more() ) {
                    if ( endToProcess ) {
                        final char ch = jsonTokener.nextClean();
                        if ( ch != '\u0000' ) {
                            throw new IOException("Not an array or document end: " + ch);
                        }
                        endToProcess = false;
                    }
                    return -1;
                }
                final char b = (char) (int) jsonTokener.nextValue();
                if ( jsonTokener.more() ) {
                    final char ch = jsonTokener.nextClean();
                    switch ( ch ) {
                    case ',':
                    case ']':
                        // do nothing
                        break;
                    default:
                        throw new IOException("Not an array delimiter: " + ch);
                    }
                }
                buffer[offset] = b;
                return 1;
            }

            @Override
            public void close()
                    throws IOException {
                reader.close();
            }
        };
    }
    ...

or using Gson for example:

    ...
    public static Reader of(final JsonReader jsonReader) {
        return new Reader() {
            private boolean beginToProcess = true;
            private boolean endToProcess = true;

            @Override
            public int read(@Nonnull final char[] buffer, final int offset, final int length)
                    throws IOException {
                if ( beginToProcess ) {
                    jsonReader.beginArray();
                    beginToProcess = false;
                }
                if ( !jsonReader.hasNext() ) {
                    if ( endToProcess ) {
                        jsonReader.endArray();
                        endToProcess = false;
                    }
                    return -1;
                }
                final int b = jsonReader.nextInt();
                buffer[offset] = (char) b;
                return 1;
            }

            @Override
            public void close()
                    throws IOException {
                jsonReader.close();
            }
        };
    }

}

Both decoders are suboptimal and do not provide the maximum performance, but I hope you won't need it once your server is fixed. Now you can decode the input readers and convert them to valid JSON documents (in a JUnit 5 test):

public final class DecodingTest {

    @Test
    public void test()
            throws IOException {
        final String payload = "[123,34,97,117,116,104,111,114,34,58,34,65,110,116,111,110,34,44,34,116,121,112,101,34,58,34,115,101,114,118,101,114,32,105,110,102,111,34,44,34,100,97,116,101,34,58,34,48,56,47,48,49,47,50,48,50,49,34,44,34,99,111,110,116,101,110,116,34,58,34,34,44,34,111,112,116,34,58,123,34,99,112,117,34,58,123,34,102,114,101,113,34,58,91,51,56,54,54,46,48,52,49,56,55,53,44,49,54,48,48,44,51,57,48,48,93,44,34,117,115,97,103,101,34,58,91,48,44,48,44,49,44,50,44,49,44,49,44,48,44,50,93,44,34,108,111,97,100,34,58,91,48,46,53,44,48,46,54,51,44,48,46,54,49,93,125,44,34,109,101,109,111,114,121,34,58,123,34,114,97,109,34,58,91,49,53,46,53,52,44,49,50,46,48,49,44,50,50,46,55,44,50,46,56,53,44,57,46,54,55,93,125,44,34,100,105,115,107,115,34,58,123,34,115,115,100,34,58,91,50,49,57,46,49,44,49,51,44,49,57,52,46,57,44,54,46,51,93,44,34,104,100,100,34,58,91,48,44,48,44,48,44,49,48,48,93,125,44,34,116,101,109,112,115,34,58,123,34,99,111,114,101,45,48,34,58,91,51,50,44,56,53,44,49,48,53,93,44,34,99,111,114,101,45,49,34,58,91,51,54,44,56,53,44,49,48,53,93,44,34,99,111,114,101,45,50,34,58,91,51,55,44,56,53,44,49,48,53,93,44,34,99,111,114,101,45,51,34,58,91,51,56,44,56,53,44,49,48,53,93,125,125,44,34,97,116,116,97,99,104,101,100,34,58,110,117,108,108,125]";
        final String expected = "{\"author\":\"Anton\",\"type\":\"server info\",\"date\":\"08/01/2021\",\"content\":\"\",\"opt\":{\"cpu\":{\"freq\":[3866.041875,1600,3900],\"usage\":[0,0,1,2,1,1,0,2],\"load\":[0.5,0.63,0.61]},\"memory\":{\"ram\":[15.54,12.01,22.7,2.85,9.67]},\"disks\":{\"ssd\":[219.1,13,194.9,6.3],\"hdd\":[0,0,0,100]},\"temps\":{\"core-0\":[32,85,105],\"core-1\":[36,85,105],\"core-2\":[37,85,105],\"core-3\":[38,85,105]}},\"attached\":null}";
        try ( final Reader reader = Decoding.of(new StringReader(payload));
                final Writer writer = new StringWriter() ) {
            CharStreams.copy(reader, writer);
            Assertions.assertEquals(expected, writer.toString());
        }
        try ( final Reader reader = Decoding.of(new JsonReader(new StringReader(payload)));
                final Writer writer = new StringWriter() ) {
            CharStreams.copy(reader, writer);
            Assertions.assertEquals(expected, writer.toString());
        }
    }

}

Note that CharStreams.copy is taken from Google Guava from brevity.

The readers can now be consumed as regular JSON documents by library, org.json, Gson, Jackson, Moshi or whatever else.

Comments

0

Besides achieving this in Jackson as demonstrated by Simeon , you can achieve the same with Gson.

Map<String, Object> decode(final byte[] src) {
    final String srcString = new String(src);
    final Type mapType = new TypeToken<Map<String, Object>>(){}.getType();
    return new Gson().fromJson(srcString, mapType);
}

In case you want to optimise the code and prevent anonymous inner classes, you can create a static inner class ...

private static class MapTypeToken extends TypeToken<Map<String, Object>> {}

... and use it in your Type variable ...

final Type mapType = new MapTypeToken().getType();

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.