Json.NET은 스트림을 직렬화 / 역 직렬화 할 수 있습니까?


151

Json.NET이 DataContractJsonSerializer보다 빠르다는 것을 들었고 시도해보고 싶었습니다 ...

그러나 JsonConvert에서 문자열 대신 스트림을 사용하는 메소드를 찾을 수 없습니다.

예를 들어, WinPhone에서 JSON을 포함하는 파일을 직렬화 해제하려면 다음 코드를 사용하여 파일 내용을 문자열로 읽은 다음 JSON으로 직렬화 해제하십시오. 스트림에서 직렬화를 해제하기 위해 DataContractJsonSerializer를 사용하는 것보다 (아주 임시) 테스트에서 약 4 배 더 느린 것으로 보입니다 ...

// DCJS
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(Constants));
Constants constants = (Constants)dc.ReadObject(stream);

// JSON.NET
string json = new StreamReader(stream).ReadToEnd();
Constants constants = JsonConvert.DeserializeObject<Constants>(json);

내가 잘못하고 있습니까?

답변:


58

업데이트 : 이것은 현재 버전에서 더 이상 작동하지 않습니다. 정답 은 아래 를 참조하십시오 ( 투표 할 필요가 없으며 이전 버전에서는 정확합니다 ).

사용 JsonTextReader로모그래퍼 클래스를 StreamReader하거나 사용 JsonSerializer걸리는 과부하 StreamReader직접 :

var serializer = new JsonSerializer();
serializer.Deserialize(streamReader);

23
이것이 더 이상 작동하지 않는다는 것을 확신하십시오. JsonReader 또는 TextReader를 사용해야합니다
— BradLaney

8
사람들이 아래로 스크롤 할 때 알 수 있도록이 버전이 계속 작동중인 버전 번호를 포함 할 수 있습니다.
— PoeHaH

@BradLaney yup JsonTextReader (givenStreamReader)는 지금가는 길입니다
— Antoine Meltzheim

작업 상태 및 답변 권장 사항에 대한 답변을 편집 해 주셔서 감사합니다
— Nick Bull

281

Json.net의 현재 버전에서는 허용되는 응답 코드를 사용할 수 없습니다. 현재 대안은 다음과 같습니다.

public static object DeserializeFromStream(Stream stream)
{
    var serializer = new JsonSerializer();

    using (var sr = new StreamReader(stream))
    using (var jsonTextReader = new JsonTextReader(sr))
    {
        return serializer.Deserialize(jsonTextReader);
    }
}

설명서 : 파일 스트림에서 JSON 직렬화 해제


4
JsonTextReader는 기본적으로 StreamReader를 닫으므로이 예제는 JsonTextReader 생성자에 대한 호출에서 StreamReader를 구성하여 약간 단순화 할 수 있습니다.
— Oliver Bock

1
이 코드와 함께 사용자 정의 변환기를 사용하는 방법을 알고 있습니까? 시리얼 라이저가 사용할 컨버터를 지정하는 방법을
— 보지 마십시오

1
실제로 OutOfMemory 예외가 있으며 이미이 코드를 사용하고 있습니다. 어느, 내가 믿는, 말을 간다, 이것이 보장되지는 않습니다 - 직렬화 복원 된 객체가 충분히 큰이며, 당신에게 32 비트 프로세스에있어 붙어 있다면, 당신은 여전히이 코드와 메모리 오류가 발생할 수 있습니다
— PandaWood

1
"유형 또는 네임 스페이스 이름 'JsonTextReader'를 찾을 수 없습니다"라는 오류 메시지가 표시됩니다. 제안 사항이 있습니까?
— hnvasa

1
stream.Position = 0;내 json을 올바르게 직렬화 해제 하기 위해 추가해야했습니다 .
— hybrid2102

76
public static void Serialize(object value, Stream s)
{
    using (StreamWriter writer = new StreamWriter(s))
    using (JsonTextWriter jsonWriter = new JsonTextWriter(writer))
    {
        JsonSerializer ser = new JsonSerializer();
        ser.Serialize(jsonWriter, value);
        jsonWriter.Flush();
    }
}

public static T Deserialize<T>(Stream s)
{
    using (StreamReader reader = new StreamReader(s))
    using (JsonTextReader jsonReader = new JsonTextReader(reader))
    {
        JsonSerializer ser = new JsonSerializer();
        return ser.Deserialize<T>(jsonReader);
    }
}

2
감사! 이것은 매우 큰 객체 컬렉션을 문자열로 직렬화 한 다음 스트림에 직접 직렬화하는 대신 해당 문자열을 스트림에 쓸 때 발생하는 OutOfMemoryException을 피하는 데 도움이되었습니다.
— Jon Schneider

2
왜 플러시? using 블록으로 인한 Dispose 호출이 이미 수행하지 않습니까?
— Şafak Gür

사용 방법 ?
— Sana

2
참고 : 다른 사용자에게 도움이 될 수 있으므로 JsonSerializer ser = JsonSerializer.Create(settings);사용하는 경우 de / serialization 중에 사용할 설정을 정의 할 수 있습니다.
— mike

1
이 Serialize구현의 한 가지 잠재적 인 문제 는 Stream전달 된 인수로 응용 프로그램에 따라 문제가 될 수있는 인수로 닫히는 것 입니다. .NET 4.5 이상 에서는 스트림을 열어 둘 수 StreamWriter있는 매개 변수와 함께 생성자 오버로드를 사용하여이 문제를 피할 leaveOpen수 있습니다.
— Joe

29

JSON 소스 (문자열, 스트림, 파일)에서 직렬화를 해제하는 데 도움이되는 확장 클래스를 작성했습니다.

public static class JsonHelpers
{
    public static T CreateFromJsonStream<T>(this Stream stream)
    {
        JsonSerializer serializer = new JsonSerializer();
        T data;
        using (StreamReader streamReader = new StreamReader(stream))
        {
            data = (T)serializer.Deserialize(streamReader, typeof(T));
        }
        return data;
    }

    public static T CreateFromJsonString<T>(this String json)
    {
        T data;
        using (MemoryStream stream = new MemoryStream(System.Text.Encoding.Default.GetBytes(json)))
        {
            data = CreateFromJsonStream<T>(stream);
        }
        return data;
    }

    public static T CreateFromJsonFile<T>(this String fileName)
    {
        T data;
        using (FileStream fileStream = new FileStream(fileName, FileMode.Open))
        {
            data = CreateFromJsonStream<T>(fileStream);
        }
        return data;
    }
}

역 직렬화는 이제 쓰기만큼 쉽습니다.

MyType obj1 = aStream.CreateFromJsonStream<MyType>();
MyType obj2 = "{\"key\":\"value\"}".CreateFromJsonString<MyType>();
MyType obj3 = "data.json".CreateFromJsonFile<MyType>();

그것이 다른 누군가를 도울 수 있기를 바랍니다.


2
반대 : 확장 방법으로 모든 문자열을 오염시킵니다. 해결 방법 : Using SomeJsonHelpersNamespace필요한 곳에만 선언 하거나 this키워드를 제거하고 JsonHelpers.CreateFromJsonString(someJsonString) Pro 를 사용하십시오. 사용하기가 훨씬 쉽습니다 :)
— Tok '

1
"오염"으로 볼 수 있지만 String 객체의 확장 중 거의 절반은 같은 방식으로 볼 수 있습니다. 이것은 문자열 (json)에서 JSON으로 지속적으로 변경되는 모든 사람에게 유용한 방식으로 객체를 확장합니다.
— vipersassassin

또한 Encoding.Default다른 컴퓨터에서 다르게 동작하므로 사용 이 좋지 않습니다 (Microsoft 문서의 큰 경고 참조). JSON은 UTF-8이어야하며 이것이 JsonSerializer가 기대하는 것입니다. 따라서이어야합니다 Encoding.UTF8. ASCII 코드가 아닌 문자를 사용하는 경우 코드는 잘못된 문자열을 생성하거나 직렬화 해제에 실패합니다.
— ckuri

17

나는이 질문에 도달하여 열린 끝의 객체 목록을 스트리밍하는 방법을 찾고 System.IO.Stream 보내기 전에 전체 목록을 버퍼링하지 않고 하여 다른 쪽 끝에서 읽을 수 . (특히 MongoDB에서 웹 API를 통해 지속 객체를 스트리밍하고 있습니다.)

@Paul Tyng과 @Rivers는 원래 질문에 대답하는 데 훌륭한 역할을했으며 내 대답에 대한 개념 증명을 작성하기 위해 답을 사용했습니다. 다른 사람이 같은 문제에 직면 한 경우 여기에 테스트 콘솔 앱을 게시하기로 결정했습니다.

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace TestJsonStream {
    class Program {
        static void Main(string[] args) {
            using(var writeStream = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.None)) {
                string pipeHandle = writeStream.GetClientHandleAsString();
                var writeTask = Task.Run(() => {
                    using(var sw = new StreamWriter(writeStream))
                    using(var writer = new JsonTextWriter(sw)) {
                        var ser = new JsonSerializer();
                        writer.WriteStartArray();
                        for(int i = 0; i < 25; i++) {
                            ser.Serialize(writer, new DataItem { Item = i });
                            writer.Flush();
                            Thread.Sleep(500);
                        }
                        writer.WriteEnd();
                        writer.Flush();
                    }
                });
                var readTask = Task.Run(() => {
                    var sw = new Stopwatch();
                    sw.Start();
                    using(var readStream = new AnonymousPipeClientStream(pipeHandle))
                    using(var sr = new StreamReader(readStream))
                    using(var reader = new JsonTextReader(sr)) {
                        var ser = new JsonSerializer();
                        if(!reader.Read() || reader.TokenType != JsonToken.StartArray) {
                            throw new Exception("Expected start of array");
                        }
                        while(reader.Read()) {
                            if(reader.TokenType == JsonToken.EndArray) break;
                            var item = ser.Deserialize<DataItem>(reader);
                            Console.WriteLine("[{0}] Received item: {1}", sw.Elapsed, item);
                        }
                    }
                });
                Task.WaitAll(writeTask, readTask);
                writeStream.DisposeLocalCopyOfClientHandle();
            }
        }

        class DataItem {
            public int Item { get; set; }
            public override string ToString() {
                return string.Format("{{ Item = {0} }}", Item);
            }
        }
    }
}

AnonymousPipeServerStream이 처리 될 때 예외가 발생할 수 있습니다 . 현재 문제와 관련이 없으므로 무시했습니다.


1
완전한 JSON 객체를 얻을 수 있도록 이것을 수정해야합니다. 내 서버와 클라이언트는 JSON 스 니펫을 전송하여 통신하므로 클라이언트는 전송할 수 {"sign in":{"username":"nick"}}{"buy item":{"_id":"32321123"}}있으며 조각을 읽을 때마다 이벤트를 알리는 두 개의 JSON 조각으로 볼 필요가 있습니다. nodejs에서 이것은 3 줄의 코드로 수행 할 수 있습니다.
— Nick Sotiros
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.