Сохраняйте порядок ключей JSON во время преобразования JSON в CSV



Я использую библиотеку JSON, предоставленную здесь http://www.json.org/java/index.html для преобразования строки json я должен CSV.
Но проблема у меня есть, порядок ключей теряется после преобразования.



это код преобразования:



    JSONObject jo = new JSONObject(someString);
JSONArray ja = jo.getJSONArray("items");
String s = CDL.toString(ja);
System.out.println(s);


это содержание "someString":



{
"items":
[
{
"WR":"qwe",
"QU":"asd",
"QA":"end",
"WO":"hasd",
"NO":"qwer"
},
]
}


вот результат:



WO,QU,WR,QA,NO
hasd,asd,qwe,end,qwer


в то время как то, что я ожидаю, чтобы сохранить порядок ключей:



WR,QU,QA,WO,NO
qwe,asd,end,hasd,qwer


есть ли как я могу получить этот результат, используя эту библиотеку? Если нет, есть ли другие библиотеки, которые дают возможность сохранить порядок ключей в результате?

888   15  

15 ответов:

есть (hacky) способы сделать это ... но ты не должен.

в JSON объект определяется следующим образом:

объект-неупорядоченный набор пар имя/значение.

см.http://json.org.

большинство реализаций JSON не прилагают никаких усилий для сохранения порядка пар имя/значение объекта, так как это (по определению) не имеет значения.

Если вы хотите, чтобы сохранить, нужно пересмотреть ваша структура данных; например,

{
    "items":
    [
        [
            {"WR":"qwe"},
            {"QU":"asd"},
            {"QA":"end"},
            {"WO":"hasd"},
            {"NO":"qwer"}
        ],
    ]
}

или проще:

{
    "items":
    [
        {"WR":"qwe"},
        {"QU":"asd"},
        {"QA":"end"},
        {"WO":"hasd"},
        {"NO":"qwer"}
    ]
}

продолжение

Спасибо за информацию, но у меня нет выбора, кроме как использовать JSON в моем приложении, и мое приложение должно поддерживать порядок ключей независимо от определения объекта JSON... Мне также не разрешено изменять формат файла JSON...

вам нужно иметь жесткий разговор с тем, кто разработал этот файл структуры и не позволит вам изменить его. Это / они просто неправы. Ты нужно чтобы убедить их.

если они действительно не позволит вам изменить его:

  • вы должны настоять на не называя его JSON ... потому что это не так.
  • вы должны указать, что вам придется писать / изменять код специально для обработки этого формата "не JSON"... если вы не можете найти некоторую реализацию JSON, которая сохраняет порядок. Если они являются платежеспособным клиентом, убедитесь, что они платят за эту дополнительную работу, которую вы должны сделать.
  • вы должны указать, что если "не JSON" должен использоваться каким-либо другим инструментом, это будет проблематично. Действительно, эта проблема будет возникать снова и снова ...

такого рода вещи, как действительно плохо. С одной стороны, ваше программное обеспечение будет нарушать хорошо установленную / давнюю спецификацию, которая предназначена для обеспечения совместимости. С другой стороны, эта гнида-ум, который проектировал этот отстой (не в формате JSON!) формат файла, вероятно, зашлаковывает чужие системы и т. д., Потому что системы не могут справиться с их бред.

обновление

также стоит прочитать, Что такое JSON RFC (RFC 7159) говорит на эту тему. Вот некоторые выдержки:

за годы, прошедшие с момента публикации RFC 4627, JSON нашел очень широкое использование. Этот опыт выявлены определенные закономерности, которые, хотя это разрешено его спецификациями, они вызвали совместимость проблемы.

JavaScript Object Notation (JSON) - это текстовый формат для сериализации структурированных данных. ...

JSON может представлять четыре примитивных типа (строки, числа, логические значения, и null) и два структурированных типа (объекты и массивы).

объект -ненумерованный коллекция из нуля или более значение name пары, где имя-строка, а значение-строка, число, boolean, null, object или array.

библиотеки синтаксического анализа JSON были замечены, чтобы отличаться относительно того, или не они делают порядок элементов объекта видимым для вызова программное обеспечение. Реализации, поведение которых не зависит от члена заказ будет интероперабельным в том смысле, что их не будет сказываются эти различия.

поддерживать порядок довольно просто. У меня была такая же проблема с поддержанием порядка от уровня БД до уровня пользовательского интерфейса.

Открыть JSONObject.Java-файл. Он внутренне использует HashMap, который не поддерживает порядок.

измените его на LinkedHashMap:

    //this.map = new HashMap();
    this.map = new LinkedHashMap();

это сработало для меня. Дайте мне знать в комментариях. Я предлагаю, чтобы сама библиотека JSON имела другой класс JSONObject, который поддерживает порядок, например JSONOrderdObject.Ява. Я очень беден выбираем имена.

JSONObject.java принимает любую карту вы проходите. Это может быть LinkedHashMap или TreeMap и это займет hashmap только когда карта имеет значение null .

вот конструктор JSONObject.java класс, который будет делать проверку карты.

 public JSONObject(Map paramMap)
  {
    this.map = (paramMap == null ? new HashMap() : paramMap);
  }

так что перед построением объекта json построить LinkedHashMap а затем передать его конструктору вот так,

LinkedHashMap<String, String> jsonOrderedMap = new LinkedHashMap<String, String>();

jsonOrderedMap.put("1","red");
jsonOrderedMap.put("2","blue");
jsonOrderedMap.put("3","green");

JSONObject orderedJson = new JSONObject(jsonOrderedMap);

JSONArray jsonArray = new JSONArray(Arrays.asList(orderedJson));

System.out.println("Ordered JSON Fianl CSV :: "+CDL.toString(jsonArray));

так что нет необходимости менять JSONObject.java класса . Надеюсь, это кому-то поможет .

Например:

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
    ],
    "itemOrder":
        ["WR", "QU", "QA", "WO", "NO"]
}

вы повторяете список itemOrder и используете их для поиска значений карты. Порядок сохранен, без клуджей.

я использовал этот метод много раз.

еще одно хакерское решение с помощью reflect:

JSONObject json = new JSONObject();
Field map = json.getClass().getDeclaredField("map");
map.setAccessible(true);//because the field is private final...
map.set(json, new LinkedHashMap<>());
map.setAccessible(false);//return flag

Апач подмигнул OrderedJSONObject. Он сохраняет порядок при разборе строки.

решена.

Я использовал JSON.простая библиотека отсюда https://code.google.com/p/json-simple/ Чтобы прочитать строку JSON, чтобы сохранить порядок ключей и использовать библиотеку JavaCSV отсюда http://sourceforge.net/projects/javacsv/ для преобразования в формат CSV.

просто наткнулся на ту же проблему, я считаю, что окончательное решение, используемое автором, состояло в использовании пользовательского ContainerFactory:

public static Values parseJSONToMap(String msgData) {
    JSONParser parser = new JSONParser();
    ContainerFactory containerFactory = new ContainerFactory(){
        @Override
        public Map createObjectContainer() {
            return new LinkedHashMap();
        }

        @Override
        public List creatArrayContainer() {
            return null;
        }
    };
    try {
        return (Map<String,Object>)parser.parse(msgData, containerFactory);
    } catch (ParseException e) {
        log.warn("Exception parsing JSON string {}", msgData, e);
    }
    return null;
}  

посмотреть http://juliusdavies.ca/json-simple-1.1.1-javadocs/org/json/simple/parser/JSONParser.html#parse(java.io.Reader,org.json.simple.parser.ContainerFactory)

Я знаю, что это решено, и вопрос был задан давно, но поскольку я имею дело с подобной проблемой, я хотел бы дать совершенно другой подход к этому:

для массивов он говорит: "массив-это упорядоченный набор значений."в http://www.json.org/ - но объекты ("объект-это неупорядоченный набор пар имя / значение.") не заказывали.

интересно, почему этот объект находится в массиве-это подразумевает порядок, который не является там.

{
"items":
[
    {
        "WR":"qwe",
        "QU":"asd",
        "QA":"end",
        "WO":"hasd",
        "NO":"qwer"
    },
]
}

таким образом, решение будет заключаться в том, чтобы поместить ключи в" реальный " массив и добавить данные в качестве объектов к каждому ключу следующим образом:

{
"items":
[
    {"WR": {"data": "qwe"}},
    {"QU": {"data": "asd"}},
    {"QA": {"data": "end"}},
    {"WO": {"data": "hasd"}},
    {"NO": {"data": "qwer"}}
]
}

таким образом, это подход, который пытается переосмыслить исходное моделирование и его цель. Но я не тестировал (и мне интересно), будут ли все задействованные инструменты сохранять порядок этого исходного массива JSON.

наиболее безопасным способом, вероятно, является метод переопределения ключей, который используется для генерации вывода:

new JSONObject(){
  @Override
  public Iterator keys(){
    TreeSet<Object> sortedKeys = new TreeSet<Object>();
    Iterator keys = super.keys();
    while(keys.hasNext()){
      sortedKeys.add(keys.next());
    }
    return sortedKeys.iterator();
  }
};

patchFor(ответ @gary):

$ git diff JSONObject.java                                                         
diff --git a/JSONObject.java b/JSONObject.java
index e28c9cd..e12b7a0 100755
--- a/JSONObject.java
+++ b/JSONObject.java
@@ -32,7 +32,7 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.Collection;
 import java.util.Enumeration;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
@@ -152,7 +152,9 @@ public class JSONObject {
      * Construct an empty JSONObject.
      */
     public JSONObject() {
-        this.map = new HashMap<String, Object>();
+//      this.map = new HashMap<String, Object>();
+        // I want to keep order of the given data:
+        this.map = new LinkedHashMap<String, Object>();
     }

     /**
@@ -243,7 +245,7 @@ public class JSONObject {
      * @throws JSONException
      */
     public JSONObject(Map<String, Object> map) {
-        this.map = new HashMap<String, Object>();
+        this.map = new LinkedHashMap<String, Object>();
         if (map != null) {
             Iterator<Entry<String, Object>> i = map.entrySet().iterator();
             while (i.hasNext()) {

вы можете использовать следующий код для выполнения пользовательской упорядоченной сериализации и десериализации массива JSON (в этом примере предполагается, что вы упорядочиваете строки, но можете применяться ко всем типам):

сериализация

JSONArray params = new JSONArray();
int paramIndex = 0;

for (String currParam : mParams)
{
    JSONObject paramObject = new JSONObject();
    paramObject.put("index", paramIndex);
    paramObject.put("value", currParam);

    params.put(paramObject);
    ++paramIndex;
}

json.put("orderedArray", params);

десериализации

JSONArray paramsJsonArray = json.optJSONArray("orderedArray");
if (null != paramsJsonArray)
{
    ArrayList<String> paramsArr = new ArrayList<>();
    for (int i = 0; i < paramsJsonArray.length(); i++)
    {
        JSONObject param = paramsJsonArray.optJSONObject(i);
        if (null != param)
        {
            int paramIndex = param.optInt("index", -1);
            String paramValue = param.optString("value", null);

            if (paramIndex > -1 && null != paramValue)
            {
                paramsArr.add(paramIndex, paramValue);
            }
        }
    }
}

ваш пример:

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
        ...
    ]
}

добавить элемент "itemorder"

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
        ...
    ],
    "itemorder":["WR","QU","QA","WO","NO"]
}

этот код генерирует желаемый результат без строки заголовка столбца:

JSONObject output = new JSONObject(json);
JSONArray docs = output.getJSONArray("data");
JSONArray names = output.getJSONArray("itemOrder");
String csv = CDL.toString(names,docs);

в реальном мире приложение почти всегда будет иметь Java bean или домен, который должен быть сериализован/де-сериализован в/из JSON. Его уже упоминалось, что спецификация объекта JSON не гарантирует порядок и любые манипуляции с этим поведением не оправдывают требования. У меня был тот же сценарий в моем приложении, где мне нужно было сохранить порядок только для целей чтения мешка. Я использовал стандартный способ Джексона для сериализации моего Java-Боба JSON:

Object object = getObject();  //the source java bean that needs conversion
String jsonString = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(object);

чтобы сделать json с упорядоченным набором элементов, я просто использую аннотацию свойства JSON в компоненте Java, который я использовал для преобразования. Пример ниже:

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"name","phone","city","id"})
public class SampleBean implements Serializable {
    private int id;
    private String name:
    private String city;
    private String phone;

    //...standard getters and setters
}

getObject () используется выше:

public SampleBean getObject(){
    SampleBean bean  = new SampleBean();
    bean.setId("100");
    bean.setName("SomeName");
    bean.setCity("SomeCity");
    bean.setPhone("1234567890");
    return bean;
}

выходные данные отображаются в соответствии с аннотацией порядка свойств Json:

{
    name: "SomeName",
    phone: "1234567890",
    city: "SomeCity",
    id: 100
}

протестировал решение подмигивания и работает нормально:

@Test
public void testJSONObject() {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("bbb", "xxx");
    jsonObject.put("ccc", "xxx");
    jsonObject.put("aaa", "xxx");
    jsonObject.put("xxx", "xxx");
    System.out.println(jsonObject.toString());
    assertTrue(jsonObject.toString().startsWith("{\"xxx\":"));
}

@Test
public void testWinkJSONObject() throws JSONException {
    org.apache.wink.json4j.JSONObject jsonObject = new OrderedJSONObject();
    jsonObject.put("bbb", "xxx");
    jsonObject.put("ccc", "xxx");
    jsonObject.put("aaa", "xxx");
    jsonObject.put("xxx", "xxx");
    assertEquals("{\"bbb\":\"xxx\",\"ccc\":\"xxx\",\"aaa\":\"xxx\",\"xxx\":\"xxx\"}", jsonObject.toString());
}

Comments

    Ничего не найдено.