Oh, добро пожаловать в великолепный мир API SimpleRide. : D У меня было счастливое время, когда я пытался решить эту проблему впервые за полтора года до запуска моего приложения для Android. Я подозреваю, что эти парни возвращают такую строку, чтобы использовать только JSON.parse
. Поэтому самый простой способ (но не самый эффективный) - это синтаксический анализ ответов в виде строк для их «нормализации», а затем анализ нормализованных документов JSON.
Для того чтобы разобрать ваш (см. Комментарии ниже) JSON, необходимо представить поток входных данных JSON в качестве потока входных строк строки JSON. Это можно сделать легко, объединив входные потоки.
final class FixedInputStreams {
private static final byte[] e1DoubleQuoteArray = "\"".getBytes();
private FixedInputStreams() {
}
static InputStream fixInputStream(final InputStream inputStream) {
return concatInputStreams(
new ByteArrayInputStream(e1DoubleQuoteArray),
inputStream,
new ByteArrayInputStream(e1DoubleQuoteArray)
);
}
private static InputStream concatInputStreams(final InputStream... inputStreams) {
return concatInputStreams(asList(inputStreams).iterator());
}
// Iterator and not an iterable by design
private static InputStream concatInputStreams(final Iterator<? extends InputStream> inputStreamsIterator) {
return new SequenceInputStream(asEnumeration(inputStreamsIterator));
}
private static <T> Enumeration<T> asEnumeration(final Iterator<T> iterator) {
return new Enumeration<T>() {
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public T nextElement() {
return iterator.next();
}
};
}
}
Что этот класс делает только фиксируя такой поток некорректного входа для имитации входного потока JSON строки. Таким образом, с входным потоком выше, ваш JSON становится юридическим JSON строка:
[{\ "X \": 24,0124010872935, \ "Y \": +49,7740722529036, \ "Код \": \ "0320 \ », \ "Имя \": .....]
→
"[{\" X \ ": 24.0124010872935, \" Y \ ": 49.7740722529036, \" Код \ ": \" 0320 \ ", \" Name \ ": .....]"
Теперь вам нужно разобрать эту полосу g для извлечения нормализованного JSON. MalformedJsonTypeAdapterFactory
представляет собой синтетическую фабрику адаптеров типа Gson, и только ответственность заключается в анализе строковых литералов JSON, а затем анализе последнего на хорошо сформированные DTO.
final class StringWrapperTypeAdapterFactory
implements TypeAdapterFactory {
private final Gson realGson;
private StringWrapperTypeAdapterFactory(final Gson realGson) {
this.realGson = realGson;
}
static TypeAdapterFactory getStringWrapperTypeAdapterFactory(final Gson realGson) {
return new StringWrapperTypeAdapterFactory(realGson);
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
return new TypeAdapter<T>() {
@Override
public void write(final JsonWriter out, final T value) {
throw new UnsupportedOperationException();
}
@Override
public T read(final JsonReader in) {
final String jsonDocument = realGson.fromJson(in, String.class);
return realGson.fromJson(jsonDocument, typeToken.getType());
}
};
}
}
Так что идея здесь:
"[{\" X \ ": 24,0124010872935, \" Y \ ": 49,7740722529036, \" Код \ ": \" 0320 \», \"Имя\": .....]»
→
[{ "X": 24,0124010872935, "Y": 49.7740722529036, "Кодекс": "0320", "Имя": .....]
образец DTO класс похож на то, что у меня в исходном коде приложения:
final class NamedPoint {
@SerializedName("X")
final double longitude = Double.valueOf(0); // disabling primitives inlining
@SerializedName("Y")
final double latitude = Double.valueOf(0);
@SerializedName("Code")
final String code = null;
@Override
public String toString() {
return '<' + code + "=(" + latitude + ',' + longitude + ")>";
// ^__ See? Even this string is aware of the issue
}
}
Наконец, общая конфигурация и рабочий процесс теперь стали как это следующим образом:
static final Type namedPointListType = new TypeToken<List<NamedPoint>>() {
}.getType();
static final Gson realGson = new GsonBuilder()
// ... your Gson configuration here ...
.create();
static final Gson stringWrapperGson = new GsonBuilder()
.registerTypeAdapterFactory(getStringWrapperTypeAdapterFactory(realGson))
.create();
// or `new ByteArrayInputStream(jsonSource.getBytes())` to test quickly
final InputStream malformedInputStream = ...;
try (final InputStream fixedInputStream = fixInputStream(malformedInputStream);
final Reader jsonReader = new BufferedReader(new InputStreamReader(fixedInputStream))) {
final List<NamedPoint> namedPoints = stringWrapperGson.fromJson(jsonReader, namedPointListType);
out.println(namedPoints);
}
Выход:
[< 0320 = (49.7740722529036,24.0124010872935)>]
Несколько замечаний относительно SimpleRide API:
- Я не уверен, что вам нужно «f ixing "на
"
-concatenation теперь, потому что API, кажется, завершает его сам (из-за JSON.parse
?). Вы можете легко проверить это с помощью wget http://82.207.107.126:13541/SimpleRide/LAD/SM.WebApi/api/Schedule/?routeId=713032&code=0298
. Может быть, определенный Content-Type
может настроить формат ответов?
- С
StringWrapperTypeAdapterFactory
создает промежуточную строку для дальнейшего анализа, это может быть неэффективно из-за затрат на память. Чтобы преодолеть эту проблему и уменьшить размер потребляемой памяти во время синтаксического анализа, вы можете написать настраиваемый InputStream
или Reader
, который может быть JSON-осведомленным и полосать экранирующие символы, поэтому вам даже не понадобится StringWrapperTypeAdapterFactory
и промежуточные строки.
Edit:
Как уже было сказано выше, потоковое фасонный стиль лучше для такого синтаксического анализа для того, чтобы сохранить память от ненужных промежуточных объектов. Несмотря, InputStream
не очень подходящее место для чтения символьные данные и Reader
подходит такая задача лучше, простой JSON-спасаясь InputStream
проще реализовать:
final class StringWrapperInputStream
extends InputStream {
private final InputStream inputStream;
private State state = State.PRE_INIT;
private StringWrapperInputStream(final InputStream inputStream) {
this.inputStream = inputStream;
}
static InputStream getStringWrapperInputStream(final InputStream inputStream) {
return new StringWrapperInputStream(inputStream);
}
@Override
public int read()
throws IOException {
for (; ;) {
switch (state) {
case PRE_INIT:
final int chPreInit = inputStream.read();
if (chPreInit == -1) {
return -1;
}
if (isWhitespace(chPreInit)) {
continue;
}
if (chPreInit == '\"') {
state = IN_PROGRESS;
} else {
throw new IllegalArgumentException("char=" + chPreInit);
}
continue;
case IN_PROGRESS:
final int chInProgress1 = inputStream.read();
if (chInProgress1 == -1) {
return -1;
}
if (chInProgress1 == '\"') {
state = DONE;
continue;
}
if (chInProgress1 != '\\') {
return chInProgress1;
}
final int chInProgress2 = inputStream.read();
if (chInProgress2 == -1) {
return -1;
}
if (chInProgress2 == '\"') {
return '\"';
}
break;
case DONE:
return -1;
default:
throw new AssertionError(state);
}
}
}
enum State {
PRE_INIT,
IN_PROGRESS,
DONE
}
}
не заменяя его работу? 'gson.fromJson (response.body(). replaceAll (" \\\ "", "\" ")' – nafas
@nafas Спасибо. Да, это работает, но мне интересно, могу ли я сделать это без такого обходного пути. верните 'replaceAll (" \\\\\ "", "\" ")' или 'replace (" \\\ "", "\" ")' – Lemberg
, чтобы быть честным помощником, если бы вы, я бы проверял, почему мой серверный пост-контент такой. Есть что-то не так. Скорее всего, клиент отправляет вам плохой формат. PS Я проверил ответ Jordi, он работал для меня (после изменения вашего ввода на 'valid' JSON, но с экранированным' ''). – nafas