在不知道类定义的情况下从JSON创建嵌套Java对象

时间:2018-03-27 06:33:54

标签: json gson

我一直在尝试使用gson将SignalK json转换为可寻址对象,但我遇到的挑战是SignalK定义有不同的方法来嵌套“Values”元素。 如果我通过使用gson.fromJson(json,MyClass.class)为每个嵌套“Values”元素的方法定义一个类,但我必须为表示“Values”的每种方式定义一个类,我才能使用它。领域似乎很复杂。 有没有办法可以使用gson(或其他东西)并生成一个跟随json模式的对象运行时?我试过用TypeToken查看泛型类型,但没有成功。 以下是不同“值”字段格式的两个示例:

示例1:

{
"context": "vessels.urn:mrn:signalk:uuid:333037323031",
"updates": [{
        "source": {
            "type": "NMEA0183",
            "talker": "GN",
            "sentence": "RMC",
            "label": "N0183-01"
        },
        "timestamp": "2018-03-21T18:50:31Z",
        "values": [{
                "path": "navigation.position",
                "value": {
                    "longitude": 09.501361,
                    "latitude": 51.763695
                }
            }
        ]
    }
]}

示例2:

{
"context": "vessels.urn:mrn:signalk:uuid:333037323031",
"updates": [{
        "source": {
            "type": "NMEA0183",
            "talker": "GN",
            "sentence": "RMC",
            "label": "N0183-01"
        },
        "timestamp": "2018-03-21T18:50:31Z",
        "values": [{
                "path": "navigation.courseOverGroundTrue",
                "value": 12.236576
            }, {
                "path": "navigation.speedOverGround",
                "value": 5.005144
            }
        ]
    }
]}

1 个答案:

答案 0 :(得分:0)

看起来您实际上正在寻找一种方法来匹配代表具有path值的类型的valueRuntimeTypeAdapterFactory存在于Gson的世界中以满足这样的要求。 这个类不是Gson的一部分,但您可以简单地下载它并将其作为代码库的一部分,以便与其余代码一起编译。

首先,定义一个简单的值类型来表示一个位置,因为它非常类似于下面示例中使用的另一个值类型java.lang.Double

final class Position {

    final double longitude = Double.valueOf(0); // Merely 0 makes javac inline this value to a constant, so we're cheating
    final double latitude = Double.valueOf(0);

    @Override
    public String toString() {
        return "(" + latitude + ";" + longitude + ")";
    }

}

然后,为简单起见,定义一组简单的属性缩减映射:

final class Wrapper {

    final List<Update> updates = null;

}

final class Update {

    final List<AbstractValue<?>> values = null;

}

abstract class AbstractValue<T> {

    final T value = null;

    @Override
    public final String toString() {
        return String.valueOf(value);
    }

}

现在我们可以定义多态值:

final class NavigationPositionValue
        extends AbstractValue<Position> {
}

final class NavigationSpeedOverGroundValue
        extends AbstractValue<Double> {
}

final class NavigationCourseOverGroundTrueValue
        extends AbstractValue<Double> {
}

现在,我们可以将path值映射到各自的预期类型,并测试构建的Gson实例:

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(RuntimeTypeAdapterFactory.of(AbstractValue.class, "path")
                .registerSubtype(NavigationPositionValue.class, "navigation.position")
                .registerSubtype(NavigationCourseOverGroundTrueValue.class, "navigation.courseOverGroundTrue")
                .registerSubtype(NavigationSpeedOverGroundValue.class, "navigation.speedOverGround")
        )
        .create();

public static void main(final String... args)
        throws IOException {
    for ( final String resourceName : ImmutableList.of("example1.json", "example2.json") ) {
        try ( final JsonReader jsonReader = Resources.getPackageResourceJsonReader(Q49505838.class, resourceName) ) {
            System.out.println(resourceName);
            final Wrapper wrapper = gson.fromJson(jsonReader, Wrapper.class);
            for ( final Update update : wrapper.updates ) {
                for ( final Object value : update.values ) {
                    System.out.println("\t" + value);
                }
            }
        }
    }
}

这将产生以下输出:

example1.json
    (51.763695;9.501361)
example2.json
    12.236576
    5.005144