2017-01-19 2 views
0

Я следующий JSON [{\"X\":24.0124010872935,\"Y\":49.7740722529036,\"Code\":\"0320\",\"Name\": .....]Как разобрать JSON, который спасся кавычки с GSON

Я пытаюсь разобрать его как

Gson gson = new Gson(); 
gson.fromJson(response.body(), RouteModel[].class) 

И получил Exception

Caused by: com.google.gson.stream.MalformedJsonException: Expected name at line 1 column 3 path $[0].

EDIT Пока лучшее решение wa s добавить compile 'org.apache.commons:commons-lang3:3.5' зависимости и использовать gson.fromJson(StringEscapeUtils.unescapeJson(response.body()), RouteModel[].class)

Или просто использовать replace("\\\"","\"")

+1

не заменяя его работу? 'gson.fromJson (response.body(). replaceAll (" \\\ "", "\" ")' – nafas

+0

@nafas Спасибо. Да, это работает, но мне интересно, могу ли я сделать это без такого обходного пути. верните 'replaceAll (" \\\\\ "", "\" ")' или 'replace (" \\\ "", "\" ")' – Lemberg

+0

, чтобы быть честным помощником, если бы вы, я бы проверял, почему мой серверный пост-контент такой. Есть что-то не так. Скорее всего, клиент отправляет вам плохой формат. PS Я проверил ответ Jordi, он работал для меня (после изменения вашего ввода на 'valid' JSON, но с экранированным' ''). – nafas

ответ

1

Использование disableHtmlEscaping должны решить эту проблему без уродливых обходных путей. Также я использовал prettyPrinting иметь более хороший выход ....

Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); 
gson.from(response.body(), RouteModel[].class) 
+0

Спасибо, что помогли мне, но похоже, что это не работает , Я получил ту же ошибку, и 'setPrettyPrinting' влияет только на сериализацию Json. Но я пытаюсь разобрать JSON на объект Java. – Lemberg

1

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 

    } 

} 
Смежные вопросы