Gson은 다형 객체 목록을 직렬화합니다.
Gson을 사용하여 다형성을 수반하는 오브젝트를 JSON으로 시리얼화/디시리얼화하려고 합니다.
시리얼화 코드는 다음과 같습니다.
ObixBaseObj lobbyObj = new ObixBaseObj();
lobbyObj.setIs("obix:Lobby");
ObixOp batchOp = new ObixOp();
batchOp.setName("batch");
batchOp.setIn("obix:BatchIn");
batchOp.setOut("obix:BatchOut");
lobbyObj.addChild(batchOp);
Gson gson = new Gson();
System.out.println(gson.toJson(lobbyObj));
결과는 다음과 같습니다.
{"obix":"obj","is":"obix:Lobby","children":[{"obix":"op","name":"batch"}]}
시리얼화는 상속된 멤버의 내용이 누락되어 있는 것을 제외하고 대부분 기능합니다(특히obix:BatchIn
그리고.obixBatchout
문자열이 없습니다).기본 클래스는 다음과 같습니다.
public class ObixBaseObj {
protected String obix;
private String display;
private String displayName;
private ArrayList<ObixBaseObj> children;
public ObixBaseObj()
{
obix = "obj";
}
public void setName(String name) {
this.name = name;
}
...
}
상속된 클래스(ObixOp)의 모양은 다음과 같습니다.
public class ObixOp extends ObixBaseObj {
private String in;
private String out;
public ObixOp() {
obix = "op";
}
public ObixOp(String in, String out) {
obix = "op";
this.in = in;
this.out = out;
}
public String getIn() {
return in;
}
public void setIn(String in) {
this.in = in;
}
public String getOut() {
return out;
}
public void setOut(String out) {
this.out = out;
}
}
이 경우 어댑터를 사용할 수 있지만 문제는 기본 클래스 유형의 컬렉션을 직렬화하는 것입니다.ObixBaseObj
. 이를 계승하는 클래스는 약 25개입니다.어떻게 하면 우아하게 할 수 있을까요?
간단한 솔루션이 있습니다: Gson's RuntimeTypeAdapterFactory (출처)com.google.code.gson:gson-extras:$gsonVersion
시리얼라이저를 쓸 필요가 없습니다.이 클래스는 모두 사용할 수 있습니다.코드를 사용하여 다음을 시도해 보십시오.
ObixBaseObj lobbyObj = new ObixBaseObj();
lobbyObj.setIs("obix:Lobby");
ObixOp batchOp = new ObixOp();
batchOp.setName("batch");
batchOp.setIn("obix:BatchIn");
batchOp.setOut("obix:BatchOut");
lobbyObj.addChild(batchOp);
RuntimeTypeAdapterFactory<ObixBaseObj> adapter =
RuntimeTypeAdapterFactory
.of(ObixBaseObj.class)
.registerSubtype(ObixBaseObj.class)
.registerSubtype(ObixOp.class);
Gson gson2=new GsonBuilder().setPrettyPrinting().registerTypeAdapterFactory(adapter).create();
Gson gson = new Gson();
System.out.println(gson.toJson(lobbyObj));
System.out.println("---------------------");
System.out.println(gson2.toJson(lobbyObj));
}
출력:
{"obix":"obj","is":"obix:Lobby","children":[{"obix":"op","name":"batch","children":[]}]}
---------------------
{
"type": "ObixBaseObj",
"obix": "obj",
"is": "obix:Lobby",
"children": [
{
"type": "ObixOp",
"in": "obix:BatchIn",
"out": "obix:BatchOut",
"obix": "op",
"name": "batch",
"children": []
}
]
}
EDIT: 더 나은 작업 예.
25개 정도의 클래스가 있다고 하셨는데ObixBaseObj
.
우리는 새로운 클래스 Gson Utils를 쓰기 시작합니다.
public class GsonUtils {
private static final GsonBuilder gsonBuilder = new GsonBuilder()
.setPrettyPrinting();
public static void registerType(
RuntimeTypeAdapterFactory<?> adapter) {
gsonBuilder.registerTypeAdapterFactory(adapter);
}
public static Gson getGson() {
return gsonBuilder.create();
}
필요할 때마다Gson
오브젝트, 호출 대신new Gson()
전화드리겠습니다.
GsonUtils.getGson()
다음 코드를 ObixBaseObj에 추가합니다.
public class ObixBaseObj {
protected String obix;
private String display;
private String displayName;
private String name;
private String is;
private ArrayList<ObixBaseObj> children = new ArrayList<ObixBaseObj>();
// new code
private static final RuntimeTypeAdapterFactory<ObixBaseObj> adapter =
RuntimeTypeAdapterFactory.of(ObixBaseObj.class);
private static final HashSet<Class<?>> registeredClasses= new HashSet<Class<?>>();
static {
GsonUtils.registerType(adapter);
}
private synchronized void registerClass() {
if (!registeredClasses.contains(this.getClass())) {
registeredClasses.add(this.getClass());
adapter.registerSubtype(this.getClass());
}
}
public ObixBaseObj() {
registerClass();
obix = "obj";
}
왜냐고요? 왜냐하면 이 수업이나 아이들 수업에서ObixBaseObj
인스턴스화 되고, 그 클래스는RuntimeTypeAdapter
자녀 클래스에서는 최소한의 변경만 필요합니다.
public class ObixOp extends ObixBaseObj {
private String in;
private String out;
public ObixOp() {
super();
obix = "op";
}
public ObixOp(String in, String out) {
super();
obix = "op";
this.in = in;
this.out = out;
}
작업 예:
public static void main(String[] args) {
ObixBaseObj lobbyObj = new ObixBaseObj();
lobbyObj.setIs("obix:Lobby");
ObixOp batchOp = new ObixOp();
batchOp.setName("batch");
batchOp.setIn("obix:BatchIn");
batchOp.setOut("obix:BatchOut");
lobbyObj.addChild(batchOp);
Gson gson = GsonUtils.getGson();
System.out.println(gson.toJson(lobbyObj));
}
출력:
{
"type": "ObixBaseObj",
"obix": "obj",
"is": "obix:Lobby",
"children": [
{
"type": "ObixOp",
"in": "obix:BatchIn",
"out": "obix:BatchOut",
"obix": "op",
"name": "batch",
"children": []
}
]
}
도움이 됐으면 좋겠어요.
커스텀 시리얼라이저/디시리얼라이저가 유일한 방법이라고 생각하며, 제가 찾은 가장 컴팩트한 방법을 제안하려고 했습니다.당신의 수업을 이용하지 못해 죄송합니다만, 같은 생각입니다(기본 클래스 1개, 확장 클래스 2개만 원했습니다).
BaseClass.java
public class BaseClass{
@Override
public String toString() {
return "BaseClass [list=" + list + ", isA=" + isA + ", x=" + x + "]";
}
public ArrayList<BaseClass> list = new ArrayList<BaseClass>();
protected String isA="BaseClass";
public int x;
}
Extended Class 1.java
public class ExtendedClass1 extends BaseClass{
@Override
public String toString() {
return "ExtendedClass1 [total=" + total + ", number=" + number
+ ", list=" + list + ", isA=" + isA + ", x=" + x + "]";
}
public ExtendedClass1(){
isA = "ExtendedClass1";
}
public Long total;
public Long number;
}
Extended Class 2(확장 클래스 2자바
public class ExtendedClass2 extends BaseClass{
@Override
public String toString() {
return "ExtendedClass2 [total=" + total + ", list=" + list + ", isA="
+ isA + ", x=" + x + "]";
}
public ExtendedClass2(){
isA = "ExtendedClass2";
}
public Long total;
}
CustomDeserializer.java
public class CustomDeserializer implements JsonDeserializer<List<BaseClass>> {
private static Map<String, Class> map = new TreeMap<String, Class>();
static {
map.put("BaseClass", BaseClass.class);
map.put("ExtendedClass1", ExtendedClass1.class);
map.put("ExtendedClass2", ExtendedClass2.class);
}
public List<BaseClass> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
List list = new ArrayList<BaseClass>();
JsonArray ja = json.getAsJsonArray();
for (JsonElement je : ja) {
String type = je.getAsJsonObject().get("isA").getAsString();
Class c = map.get(type);
if (c == null)
throw new RuntimeException("Unknow class: " + type);
list.add(context.deserialize(je, c));
}
return list;
}
}
CustomSerializer.java
public class CustomSerializer implements JsonSerializer<ArrayList<BaseClass>> {
private static Map<String, Class> map = new TreeMap<String, Class>();
static {
map.put("BaseClass", BaseClass.class);
map.put("ExtendedClass1", ExtendedClass1.class);
map.put("ExtendedClass2", ExtendedClass2.class);
}
@Override
public JsonElement serialize(ArrayList<BaseClass> src, Type typeOfSrc,
JsonSerializationContext context) {
if (src == null)
return null;
else {
JsonArray ja = new JsonArray();
for (BaseClass bc : src) {
Class c = map.get(bc.isA);
if (c == null)
throw new RuntimeException("Unknow class: " + bc.isA);
ja.add(context.serialize(bc, c));
}
return ja;
}
}
}
모든 것을 테스트하기 위해 실행한 코드는 다음과 같습니다.
public static void main(String[] args) {
BaseClass c1 = new BaseClass();
ExtendedClass1 e1 = new ExtendedClass1();
e1.total = 100L;
e1.number = 5L;
ExtendedClass2 e2 = new ExtendedClass2();
e2.total = 200L;
e2.x = 5;
BaseClass c2 = new BaseClass();
c1.list.add(e1);
c1.list.add(e2);
c1.list.add(c2);
List<BaseClass> al = new ArrayList<BaseClass>();
// this is the instance of BaseClass before serialization
System.out.println(c1);
GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(al.getClass(), new CustomDeserializer());
gb.registerTypeAdapter(al.getClass(), new CustomSerializer());
Gson gson = gb.create();
String json = gson.toJson(c1);
// this is the corresponding json
System.out.println(json);
BaseClass newC1 = gson.fromJson(json, BaseClass.class);
System.out.println(newC1);
}
다음은 나의 실행입니다.
BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0]
{"list":[{"total":100,"number":5,"list":[],"isA":"ExtendedClass1","x":0},{"total":200,"list":[],"isA":"ExtendedClass2","x":5},{"list":[],"isA":"BaseClass","x":0}],"isA":"BaseClass","x":0}
BaseClass [list=[ExtendedClass1 [total=100, number=5, list=[], isA=ExtendedClass1, x=0], ExtendedClass2 [total=200, list=[], isA=ExtendedClass2, x=5], BaseClass [list=[], isA=BaseClass, x=0]], isA=BaseClass, x=0]
몇 가지 설명: 이 트릭은 시리얼라이저/디시리얼라이저 내부의 다른 Gson에 의해 실행됩니다.저는 그냥 쓰고 있어요isA
적절한 클래스를 찾을 수 있습니다.더 빨리 이동하기 위해 지도를 사용하여isA
대응하는 클래스에 스트링을 지정합니다.그런 다음 두 번째 Gson 객체를 사용하여 적절한 시리얼화/디시리얼화를 수행합니다.Gson을 여러 개 할당해도 시리얼화/디시리얼화 속도가 느려지지 않도록 정적임을 선언했습니다.
장점 실제로는 이보다 더 많은 코드를 작성하지 않고 Gson에게 모든 작업을 맡깁니다.맵에 새로운 서브클래스를 추가하는 것(예외는 그 점을 상기시킵니다)만 기억하면 됩니다.
단점 맵이 2개 있습니다.맵의 중복을 피하기 위해 실장을 조금 개선할 수 있다고 생각합니다만, 고객(또는 향후의 에디터(있는 경우)에게 맡겼습니다.
객체로 " "를 .TypeAdapter
클래스 또는 두 인터페이스를 모두 구현하는 오브젝트로 실험합니다.
이 문제를 해결하는 데 도움이 된 다른 답변에 감사드립니다.는 ★★★★★★★★★★★★★★★★★★★★★★★★★★RuntimeTypeAdapterFactory
반사와 함께.
또한 적절하게 설정된 Gson이 사용되었는지 확인하기 위해 도우미 클래스를 만들었습니다.
GsonHelper 클래스 내의 정적 블록 내에서 다음 코드를 사용하여 적절한 유형을 모두 검색 및 등록합니다.JSON-ification을 통과하는 모든 객체는 JSonable의 하위 유형입니다.다음을 변경할 수 있습니다.
- in my.project in.
Reflections
패키지 이름이어야 합니다. Jsonable.class
베이스 클래스입니다.네 것으로 대체해.필드에 완전한 표준 이름을 표시하는 것은 좋지만, 필요하지 않은 경우 호출의 해당 부분을 생략하고 하위 유형을 등록할 수 있습니다.입니다.
className
RuntimeAdapterFactory
입니다. ; ; ; ; ; ; ; ; ; 。type
syslog.syslog.syslog.private static final GsonBuilder gsonBuilder = new GsonBuilder() .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") .excludeFieldsWithoutExposeAnnotation() .setPrettyPrinting(); static { Reflections reflections = new Reflections("my.project"); Set<Class<? extends Jsonable>> allTypes = reflections.getSubTypesOf(Jsonable.class); for (Class< ? extends Jsonable> serClass : allTypes){ Set<?> subTypes = reflections.getSubTypesOf(serClass); if (subTypes.size() > 0){ RuntimeTypeAdapterFactory<?> adapterFactory = RuntimeTypeAdapterFactory.of(serClass, "className"); for (Object o : subTypes ){ Class c = (Class)o; adapterFactory.registerSubtype(c, c.getCanonicalName()); } gsonBuilder.registerTypeAdapterFactory(adapterFactory); } } } public static Gson getGson() { return gsonBuilder.create(); }
주석과 ClassGraph를 사용하여 서브클래스를 검출하고 여러 시리얼라이제이션 스타일(Type Property, Property, Array)을 지원하는 타입 어댑터 팩토리를 만들었습니다.소스 코드와 메이븐 좌표는 github을 참조하십시오.
언급URL : https://stackoverflow.com/questions/19588020/gson-serialize-a-list-of-polymorphic-objects
'source' 카테고리의 다른 글
"쉼표 연산자의 왼쪽..렌더링의 html 내용에 오류가 있습니다. (0) | 2023.03.12 |
---|---|
TypeScript의 새로운 "as" 연산자와 유형 어사션 사이에 어떤 차이가 있습니까? (0) | 2023.03.12 |
커스텀 이미지 처리/관리 방법 (0) | 2023.03.12 |
Mac OS X에서 mongod를 멈추는 깨끗한 방법은 무엇입니까? (0) | 2023.03.07 |
키 값으로 JSON 필터링 (0) | 2023.03.07 |